/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    sertl.c

Abstract:

    This Module implements many security rtl routines defined in ntseapi.h

Author:

    Jim Kelly       (JimK)     23-Mar-1990
    Robert Reichel  (RobertRe)  1-Mar-1991

Environment:

    Pure Runtime Library Routine

Revision History:


--*/


#include "ntrtlp.h"
#include <stdio.h>
#include "seopaque.h"
#include "sertlp.h"

//
// BUG, BUG does anybody use this routine - no prototype in ntrtl.h
//

ULONG
RtlLengthUsedSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor
    );

#undef RtlEqualLuid

NTSYSAPI
BOOLEAN
NTAPI
RtlEqualLuid (
    PLUID Luid1,
    PLUID Luid2
    );

#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
#pragma alloc_text(PAGE,RtlRunEncodeUnicodeString)
#pragma alloc_text(PAGE,RtlRunDecodeUnicodeString)
#pragma alloc_text(PAGE,RtlEraseUnicodeString)
#pragma alloc_text(PAGE,RtlAdjustPrivilege)
#pragma alloc_text(PAGE,RtlValidSid)
#pragma alloc_text(PAGE,RtlEqualSid)
#pragma alloc_text(PAGE,RtlEqualPrefixSid)
#pragma alloc_text(PAGE,RtlLengthRequiredSid)
#pragma alloc_text(PAGE,RtlAllocateAndInitializeSid)
#pragma alloc_text(PAGE,RtlInitializeSid)
#pragma alloc_text(PAGE,RtlFreeSid)
#pragma alloc_text(PAGE,RtlIdentifierAuthoritySid)
#pragma alloc_text(PAGE,RtlSubAuthoritySid)
#pragma alloc_text(PAGE,RtlSubAuthorityCountSid)
#pragma alloc_text(PAGE,RtlLengthSid)
#pragma alloc_text(PAGE,RtlCopySid)
#pragma alloc_text(PAGE,RtlCopySidAndAttributesArray)
#pragma alloc_text(PAGE,RtlConvertSidToUnicodeString)
#pragma alloc_text(PAGE,RtlEqualLuid)
#pragma alloc_text(PAGE,RtlCopyLuid)
#pragma alloc_text(PAGE,RtlCopyLuidAndAttributesArray)
#pragma alloc_text(PAGE,RtlCreateSecurityDescriptor)
#pragma alloc_text(PAGE,RtlValidSecurityDescriptor)
#pragma alloc_text(PAGE,RtlLengthSecurityDescriptor)
#pragma alloc_text(PAGE,RtlLengthUsedSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetControlSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetDaclSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetDaclSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetSaclSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetSaclSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetOwnerSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetOwnerSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetGroupSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetGroupSecurityDescriptor)
#pragma alloc_text(PAGE,RtlAreAllAccessesGranted)
#pragma alloc_text(PAGE,RtlAreAnyAccessesGranted)
#pragma alloc_text(PAGE,RtlMapGenericMask)
#pragma alloc_text(PAGE,RtlpApplyAclToObject)
#pragma alloc_text(PAGE,RtlpContainsCreatorGroupSid)
#pragma alloc_text(PAGE,RtlpContainsCreatorOwnerSid)
#pragma alloc_text(PAGE,RtlpGenerateInheritAcl)
#pragma alloc_text(PAGE,RtlpGenerateInheritedAce)
#pragma alloc_text(PAGE,RtlpInheritAcl)
#pragma alloc_text(PAGE,RtlpLengthInheritAcl)
#pragma alloc_text(PAGE,RtlpLengthInheritedAce)
#pragma alloc_text(PAGE,RtlpValidOwnerSubjectContext)
#endif



///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//    Local Macros and Symbols                                               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////


#define CREATOR_SID_SIZE 12


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//    Exported Procedures                                                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////


VOID
RtlRunEncodeUnicodeString(
    PUCHAR          Seed        OPTIONAL,
    PUNICODE_STRING String
    )

/*++

Routine Description:

    This function performs a trivial XOR run-encoding of a string.
    The purpose of this run-encoding is to change the character values
    to appear somewhat random and typically not printable.  This is
    useful for transforming passwords that you don't want to be easily
    distinguishable by visually scanning a paging file or memory dump.


Arguments:

    Seed - Points to a seed value to use in the encoding.  If the
        pointed to value is zero, then this routine will assign
        a value.

    String - The string to encode.  This string may be decode
        by passing it and the seed value to RtlRunDecodeUnicodeString().


Return Value:

    None - Nothing can really go wrong unless the caller passes bogus
        parameters.  In this case, the caller can catch the access
        violation.


--*/
{

    LARGE_INTEGER Time;
    PUCHAR        LocalSeed;
    NTSTATUS      Status;
    ULONG         i;
    PSTRING       S;


    RTL_PAGED_CODE();

    //
    // Typecast so we can work on bytes rather than WCHARs
    //

    S = (PSTRING)((PVOID)String);

    //
    // If a seed wasn't passed, use the 2nd byte of current time.
    // This byte seems to be sufficiently random (by observation).
    //

    if ((*Seed) == 0) {
        Status = NtQuerySystemTime ( &Time );
        ASSERT(NT_SUCCESS(Status));

        LocalSeed = (PUCHAR)((PVOID)&Time);

        i = 1;

        (*Seed) = LocalSeed[ i ];

        //
        // Occasionally, this byte could be zero.  That would cause the
        // string to become un-decodable, since 0 is the magic value that
        // causes us to re-gen the seed.  This loop makes sure that we
        // never end up with a zero byte (unless time is zero, as well).
        //

        while ( ((*Seed) == 0) && ( i < sizeof( Time ) ) )
        {
            (*Seed) |= LocalSeed[ i++ ] ;
        }

        if ( (*Seed) == 0 )
        {
            (*Seed) = 1;
        }
    }

    //
    // Transform the initial byte.
    // The funny constant just keeps the first byte from propagating
    // into the second byte in the next step.  Without a funny constant
    // this would happen for many languages (which typically have every
    // other byte zero.
    //
    //

    if (S->Length >= 1) {
        S->Buffer[0] ^= ((*Seed) | 0X43);
    }


    //
    // Now transform the rest of the string
    //

    for (i=1; i<S->Length; i++) {

        //
        //  There are export issues that cause us to want to
        //  keep this algorithm simple.  Please don't change it
        //  without checking with JimK first.  Thanks.
        //

        //
        // In order to be compatible with zero terminated unicode strings,
        //  this algorithm is designed to not produce a wide character of
        //  zero as long a the seed is not zero.
        //

        //
        // Simple running XOR with the previous byte and the
        // seed value.
        //

        S->Buffer[i] ^= (S->Buffer[i-1]^(*Seed));

    }


    return;

}


VOID
RtlRunDecodeUnicodeString(
    UCHAR           Seed,
    PUNICODE_STRING String
    )
/*++

Routine Description:

    This function performs the inverse of the function performed
    by RtlRunEncodeUnicodeString().  Please see RtlRunEncodeUnicodeString()
    for details.


Arguments:

    Seed - The seed value to use in RtlRunEncodeUnicodeString().

    String - The string to reveal.


Return Value:

    None - Nothing can really go wrong unless the caller passes bogus
        parameters.  In this case, the caller can catch the access
        violation.


--*/

{

    ULONG
        i;

    PSTRING
        S;

    RTL_PAGED_CODE();

    //
    // Typecast so we can work on bytes rather than WCHARs
    //

    S = (PSTRING)((PVOID)String);


    //
    // Transform the end of the string
    //

    for (i=S->Length; i>1; i--) {

        //
        // a simple running XOR with the previous byte and the
        // seed value.
        //

        S->Buffer[i-1] ^= (S->Buffer[i-2]^Seed);

    }

    //
    // Finally, transform the initial byte
    //

    if (S->Length >= 1) {
        S->Buffer[0] ^= (Seed | 0X43);
    }


    return;
}



VOID
RtlEraseUnicodeString(
    PUNICODE_STRING String
    )
/*++

Routine Description:

    This function scrubs the passed string by over-writing all
    characters in the string.  The entire string (i.e., MaximumLength)
    is erased, not just the current length.


Argumen ts:

    String - The string to be erased.


Return Value:

    None - Nothing can really go wrong unless the caller passes bogus
        parameters.  In this case, the caller can catch the access
        violation.


--*/

{
    RTL_PAGED_CODE();

    if ((String->Buffer == NULL) || (String->MaximumLength == 0)) {
        return;
    }

    RtlZeroMemory( (PVOID)String->Buffer, (ULONG)String->MaximumLength );

    String->Length = 0;

    return;
}



NTSTATUS
RtlAdjustPrivilege(
    ULONG Privilege,
    BOOLEAN Enable,
    BOOLEAN Client,
    PBOOLEAN WasEnabled
    )

/*++

Routine Description:

    This procedure enables or disables a privilege process-wide.

Arguments:

    Privilege - The lower 32-bits of the privilege ID to be enabled or
        disabled.  The upper 32-bits is assumed to be zero.

    Enable - A boolean indicating whether the privilege is to be enabled
        or disabled.  TRUE indicates the privilege is to be enabled.
        FALSE indicates the privilege is to be disabled.

    Client - A boolean indicating whether the privilege should be adjusted
        in a client token or the process's own token.   TRUE indicates
        the client's token should be used (and an error returned if there
        is no client token).  FALSE indicates the process's token should
        be used.

    WasEnabled - points to a boolean to receive an indication of whether
        the privilege was previously enabled or disabled.  TRUE indicates
        the privilege was previously enabled.  FALSE indicates the privilege
        was previoulsy disabled.  This value is useful for returning the
        privilege to its original state after using it.


Return Value:

    STATUS_SUCCESS - The privilege has been sucessfully enabled or disabled.

    STATUS_PRIVILEGE_NOT_HELD - The privilege is not held by the specified context.

    Other status values as may be returned by:

            NtOpenProcessToken()
            NtAdjustPrivilegesToken()


--*/

{
    NTSTATUS
        Status,
        TmpStatus;

    HANDLE
        Token;

    LUID
        LuidPrivilege;

    PTOKEN_PRIVILEGES
        NewPrivileges,
        OldPrivileges;

    ULONG
        Length;

    UCHAR
        Buffer1[sizeof(TOKEN_PRIVILEGES)+
                ((1-ANYSIZE_ARRAY)*sizeof(LUID_AND_ATTRIBUTES))],
        Buffer2[sizeof(TOKEN_PRIVILEGES)+
                ((1-ANYSIZE_ARRAY)*sizeof(LUID_AND_ATTRIBUTES))];


    RTL_PAGED_CODE();

    NewPrivileges = (PTOKEN_PRIVILEGES)Buffer1;
    OldPrivileges = (PTOKEN_PRIVILEGES)Buffer2;

    //
    // Open the appropriate token...
    //

    if (Client == TRUE) {
        Status = NtOpenThreadToken(
                     NtCurrentThread(),
                     TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                     FALSE,
                     &Token
                     );
    } else {

        Status = NtOpenProcessToken(
                     NtCurrentProcess(),
                     TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                     &Token
                    );
    }

    if (!NT_SUCCESS(Status)) {
        return(Status);
    }



    //
    // Initialize the privilege adjustment structure
    //

    LuidPrivilege = RtlConvertUlongToLuid(Privilege);


    NewPrivileges->PrivilegeCount = 1;
    NewPrivileges->Privileges[0].Luid = LuidPrivilege;
    NewPrivileges->Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0;



    //
    // Adjust the privilege
    //

    Status = NtAdjustPrivilegesToken(
                 Token,                     // TokenHandle
                 FALSE,                     // DisableAllPrivileges
                 NewPrivileges,             // NewPrivileges
                 sizeof(Buffer1),           // BufferLength
                 OldPrivileges,             // PreviousState (OPTIONAL)
                 &Length                    // ReturnLength
                 );


    TmpStatus = NtClose(Token);
    ASSERT(NT_SUCCESS(TmpStatus));


    //
    // Map the success code NOT_ALL_ASSIGNED to an appropriate error
    // since we're only trying to adjust the one privilege.
    //

    if (Status == STATUS_NOT_ALL_ASSIGNED) {
        Status = STATUS_PRIVILEGE_NOT_HELD;
    }


    if (NT_SUCCESS(Status)) {

        //
        // If there are no privileges in the previous state, there were
        // no changes made. The previous state of the privilege
        // is whatever we tried to change it to.
        //

        if (OldPrivileges->PrivilegeCount == 0) {

            (*WasEnabled) = Enable;

        } else {

            (*WasEnabled) =
                (OldPrivileges->Privileges[0].Attributes & SE_PRIVILEGE_ENABLED)
                ? TRUE : FALSE;
        }
    }

    return(Status);
}


BOOLEAN
RtlValidSid (
    IN PSID Sid
    )

/*++

Routine Description:

    This procedure validates an SID's structure.

Arguments:

    Sid - Pointer to the SID structure to validate.

Return Value:

    BOOLEAN - TRUE if the structure of Sid is valid.

--*/

{
    RTL_PAGED_CODE();

    //
    // Make sure revision is SID_REVISION and sub authority count is not
    // greater than maximum number of allowed sub-authorities.
    //

    try {

        if ((((SID *)Sid)->Revision & 0x0f) == SID_REVISION) {
          if (((SID *)Sid)->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES) {
             return TRUE;
          }
        }

    } except(EXCEPTION_EXECUTE_HANDLER) {
        return FALSE;
    }

    return FALSE;

}



BOOLEAN
RtlEqualSid (
    IN PSID Sid1,
    IN PSID Sid2
    )

/*++

Routine Description:

    This procedure tests two SID values for equality.

Arguments:

    Sid1, Sid2 - Supply pointers to the two SID values to compare.
        The SID structures are assumed to be valid.

Return Value:

    BOOLEAN - TRUE if the value of Sid1 is equal to Sid2, and FALSE
        otherwise.

--*/

{
   ULONG SidLength;

   RTL_PAGED_CODE();

   //
   // Make sure they are the same revision
   //

   if ( ((SID *)Sid1)->Revision == ((SID *)Sid2)->Revision ) {

       //
       // Check the SubAuthorityCount first, because it's fast and
       // can help us exit faster.
       //

       if ( *RtlSubAuthorityCountSid( Sid1 ) == *RtlSubAuthorityCountSid( Sid2 )) {

           SidLength = SeLengthSid( Sid1 );
           return( (BOOLEAN)RtlEqualMemory( Sid1, Sid2, SidLength) );
       }
   }

   return( FALSE );

}



BOOLEAN
RtlEqualPrefixSid (
    IN PSID Sid1,
    IN PSID Sid2
    )

/*++

Routine Description:

    This procedure tests two SID prefix values for equality.

    An SID prefix is the entire SID except for the last sub-authority
    value.

Arguments:

    Sid1, Sid2 - Supply pointers to the two SID values to compare.
        The SID structures are assumed to be valid.

Return Value:

    BOOLEAN - TRUE if the prefix value of Sid1 is equal to Sid2, and FALSE
        otherwise.

--*/


{
    LONG Index;

    //
    // Typecast to the opaque SID structures.
    //

    SID *ISid1 = Sid1;
    SID *ISid2 = Sid2;

    RTL_PAGED_CODE();

    //
    // Make sure they are the same revision
    //

    if (ISid1->Revision == ISid2->Revision ) {

        //
        // Compare IdentifierAuthority values
        //

        if ( (ISid1->IdentifierAuthority.Value[0] ==
              ISid2->IdentifierAuthority.Value[0])  &&
             (ISid1->IdentifierAuthority.Value[1]==
              ISid2->IdentifierAuthority.Value[1])  &&
             (ISid1->IdentifierAuthority.Value[2] ==
              ISid2->IdentifierAuthority.Value[2])  &&
             (ISid1->IdentifierAuthority.Value[3] ==
              ISid2->IdentifierAuthority.Value[3])  &&
             (ISid1->IdentifierAuthority.Value[4] ==
              ISid2->IdentifierAuthority.Value[4])  &&
             (ISid1->IdentifierAuthority.Value[5] ==
              ISid2->IdentifierAuthority.Value[5])
            ) {

            //
            // Compare SubAuthorityCount values
            //

            if (ISid1->SubAuthorityCount == ISid2->SubAuthorityCount) {

                if (ISid1->SubAuthorityCount == 0) {
                    return TRUE;
                }

                Index = 0;
                while (Index < (ISid1->SubAuthorityCount - 1)) {
                    if ((ISid1->SubAuthority[Index]) != (ISid2->SubAuthority[Index])) {

                        //
                        // Found some SubAuthority values that weren't equal.
                        //

                        return FALSE;
                    }
                    Index += 1;
                }

                //
                // All SubAuthority values are equal.
                //

                return TRUE;
            }
        }
    }

    //
    // Either the Revision, SubAuthorityCount, or IdentifierAuthority values
    // weren't equal.
    //

    return FALSE;
}



ULONG
RtlLengthRequiredSid (
    IN ULONG SubAuthorityCount
    )

/*++

Routine Description:

    This routine returns the length, in bytes, required to store an SID
    with the specified number of Sub-Authorities.

Arguments:

    SubAuthorityCount - The number of sub-authorities to be stored in the SID.

Return Value:

    ULONG - The length, in bytes, required to store the SID.


--*/

{
    RTL_PAGED_CODE();

    return (8L + (4 * SubAuthorityCount));

}


NTSTATUS
RtlAllocateAndInitializeSid(
    IN PSID_IDENTIFIER_AUTHORITY IdentifierAuthority,
    IN UCHAR SubAuthorityCount,
    IN ULONG SubAuthority0,
    IN ULONG SubAuthority1,
    IN ULONG SubAuthority2,
    IN ULONG SubAuthority3,
    IN ULONG SubAuthority4,
    IN ULONG SubAuthority5,
    IN ULONG SubAuthority6,
    IN ULONG SubAuthority7,
    OUT PSID *Sid
    )

/*++

Routine Description:

    This function allocates and initializes a sid with the specified
    number of sub-authorities (up to 8).  A sid allocated with this
    routine must be freed using RtlFreeSid().

    THIS ROUTINE IS CURRENTLY NOT CALLABLE FROM KERNEL MODE.

Arguments:

    IdentifierAuthority - Pointer to the Identifier Authority value to
        set in the SID.

    SubAuthorityCount - The number of sub-authorities to place in the SID.
        This also identifies how many of the SubAuthorityN parameters
        have meaningful values.  This must contain a value from 0 through
        8.

    SubAuthority0-7 - Provides the corresponding sub-authority value to
        place in the SID.  For example, a SubAuthorityCount value of 3
        indicates that SubAuthority0, SubAuthority1, and SubAuthority0
        have meaningful values and the rest are to be ignored.

    Sid - Receives a pointer to the SID data structure to initialize.

Return Value:

    STATUS_SUCCESS - The SID has been allocated and initialized.

    STATUS_NO_MEMORY - The attempt to allocate memory for the SID
        failed.

    STATUS_INVALID_SID - The number of sub-authorities specified did
        not fall in the valid range for this api (0 through 8).


--*/
{
    PISID ISid;

    RTL_PAGED_CODE();

    if ( SubAuthorityCount > 8 ) {
        return( STATUS_INVALID_SID );
    }

    ISid = RtlAllocateHeap( RtlProcessHeap(), 0,
                            RtlLengthRequiredSid(SubAuthorityCount)
                            );
    if (ISid == NULL) {
        return(STATUS_NO_MEMORY);
    }

    ISid->SubAuthorityCount = (UCHAR)SubAuthorityCount;
    ISid->Revision = 1;
    ISid->IdentifierAuthority = *IdentifierAuthority;

    switch (SubAuthorityCount) {

    case 8:
        ISid->SubAuthority[7] = SubAuthority7;
    case 7:
        ISid->SubAuthority[6] = SubAuthority6;
    case 6:
        ISid->SubAuthority[5] = SubAuthority5;
    case 5:
        ISid->SubAuthority[4] = SubAuthority4;
    case 4:
        ISid->SubAuthority[3] = SubAuthority3;
    case 3:
        ISid->SubAuthority[2] = SubAuthority2;
    case 2:
        ISid->SubAuthority[1] = SubAuthority1;
    case 1:
        ISid->SubAuthority[0] = SubAuthority0;
    case 0:
        ;
    }

    (*Sid) = ISid;
    return( STATUS_SUCCESS );

}



NTSTATUS
RtlInitializeSid(
    IN PSID Sid,
    IN PSID_IDENTIFIER_AUTHORITY IdentifierAuthority,
    IN UCHAR SubAuthorityCount
    )
/*++

Routine Description:

    This function initializes an SID data structure.  It does not, however,
    set the sub-authority values.  This must be done separately.

Arguments:

    Sid - Pointer to the SID data structure to initialize.

    IdentifierAuthority - Pointer to the Identifier Authority value to
        set in the SID.

    SubAuthorityCount - The number of sub-authorities that will be placed in
        the SID (a separate action).

Return Value:


--*/
{
    PISID ISid;

    RTL_PAGED_CODE();

    //
    //  Typecast to the opaque SID
    //

    ISid = (PISID)Sid;

    if ( SubAuthorityCount > SID_MAX_SUB_AUTHORITIES ) {
        return( STATUS_INVALID_PARAMETER );
    }

    ISid->SubAuthorityCount = (UCHAR)SubAuthorityCount;
    ISid->Revision = 1;
    ISid->IdentifierAuthority = *IdentifierAuthority;

    return( STATUS_SUCCESS );

}


PVOID
RtlFreeSid(
    IN PSID Sid
    )

/*++

Routine Description:

    This function is used to free a SID previously allocated using
    RtlAllocateAndInitializeSid().

    THIS ROUTINE IS CURRENTLY NOT CALLABLE FROM KERNEL MODE.

Arguments:

    Sid - Pointer to the SID to free.

Return Value:

    None.


--*/
{
    RTL_PAGED_CODE();

    if (RtlFreeHeap( RtlProcessHeap(), 0, Sid ))
        return NULL;
    else
        return Sid;
}


PSID_IDENTIFIER_AUTHORITY
RtlIdentifierAuthoritySid(
    IN PSID Sid
    )
/*++

Routine Description:

    This function returns the address of an SID's IdentifierAuthority field.

Arguments:

    Sid - Pointer to the SID data structure.

Return Value:


--*/
{
    PISID ISid;

    RTL_PAGED_CODE();

    //
    //  Typecast to the opaque SID
    //

    ISid = (PISID)Sid;

    return &(ISid->IdentifierAuthority);

}

PULONG
RtlSubAuthoritySid(
    IN PSID Sid,
    IN ULONG SubAuthority
    )
/*++

Routine Description:

    This function returns the address of a sub-authority array element of
    an SID.

Arguments:

    Sid - Pointer to the SID data structure.

    SubAuthority - An index indicating which sub-authority is being specified.
        This value is not compared against the number of sub-authorities in the
        SID for validity.

Return Value:


--*/
{
    PISID ISid;

    RTL_PAGED_CODE();

    //
    //  Typecast to the opaque SID
    //

    ISid = (PISID)Sid;

    return &(ISid->SubAuthority[SubAuthority]);

}

PUCHAR
RtlSubAuthorityCountSid(
    IN PSID Sid
    )
/*++

Routine Description:

    This function returns the address of the sub-authority count field of
    an SID.

Arguments:

    Sid - Pointer to the SID data structure.

Return Value:


--*/
{
    PISID ISid;

    RTL_PAGED_CODE();

    //
    //  Typecast to the opaque SID
    //

    ISid = (PISID)Sid;

    return &(ISid->SubAuthorityCount);

}

ULONG
RtlLengthSid (
    IN PSID Sid
    )

/*++

Routine Description:

    This routine returns the length, in bytes, of a structurally valid SID.

Arguments:

    Sid - Points to the SID whose length is to be returned.  The
        SID's structure is assumed to be valid.

Return Value:

    ULONG - The length, in bytes, of the SID.


--*/

{
    RTL_PAGED_CODE();

    return SeLengthSid(Sid);
}


NTSTATUS
RtlCopySid (
    IN ULONG DestinationSidLength,
    OUT PSID DestinationSid,
    IN PSID SourceSid
    )

/*++

Routine Description:

    This routine copies the value of the source SID to the destination
    SID.

Arguments:

    DestinationSidLength - Indicates the length, in bytes, of the
        destination SID buffer.

    DestinationSid - Pointer to a buffer to receive a copy of the
        source Sid value.

    SourceSid - Supplies the Sid value to be copied.

Return Value:

    STATUS_SUCCESS - Indicates the SID was successfully copied.

    STATUS_BUFFER_TOO_SMALL - Indicates the target buffer wasn't
        large enough to receive a copy of the SID.


--*/

{
    ULONG SidLength = SeLengthSid(SourceSid);

    RTL_PAGED_CODE();

    if (SidLength > DestinationSidLength) {

        return STATUS_BUFFER_TOO_SMALL;

    }

    //
    // Buffer is large enough
    //

    RtlMoveMemory( DestinationSid, SourceSid, SidLength );

    return STATUS_SUCCESS;

}


NTSTATUS
RtlCopySidAndAttributesArray (
    IN ULONG ArrayLength,
    IN PSID_AND_ATTRIBUTES Source,
    IN ULONG TargetSidBufferSize,
    OUT PSID_AND_ATTRIBUTES TargetArrayElement,
    OUT PSID TargetSid,
    OUT PSID *NextTargetSid,
    OUT PULONG RemainingTargetSidBufferSize
    )

/*++

Routine Description:

    This routine copies the value of the source SID_AND_ATTRIBUTES array
    to the target.  The actual SID values are placed according to a separate
    parameter.  This allows multiple arrays to be merged using this service
    to copy each.

Arguments:

    ArrayLength - Number of elements in the source array to copy.

    Source - Pointer to the source array.

    TargetSidBufferSize - Indicates the length, in bytes, of the buffer
        to receive the actual SID values.  If this value is less than
        the actual amount needed, then STATUS_BUFFER_TOO_SMALL is returned.

    TargetArrayElement - Indicates where the array elements are to be
        copied to (but not the SID values themselves).

    TargetSid - Indicates where the target SID values s are to be copied.  This
        is assumed to be ULONG aligned.  Each SID value will be copied
        into this buffer.  Each SID will be ULONG aligned.

    NextTargetSid - On completion, will be set to point to the ULONG
        aligned address following the last SID copied.

    RemainingTargetSidBufferSize - On completion, receives an indicatation
        of how much of the SID buffer is still unused.


Return Value:

    STATUS_SUCCESS - The call completed successfully.

    STATUS_BUFFER_TOO_SMALL - Indicates the buffer to receive the SID
        values wasn't large enough.



--*/

{

    ULONG Index = 0;
    PSID NextSid = TargetSid;
    ULONG NextSidLength;
    ULONG AlignedSidLength;
    ULONG RemainingLength = TargetSidBufferSize;

    RTL_PAGED_CODE();

    while (Index < ArrayLength) {

        NextSidLength = SeLengthSid( Source[Index].Sid );
        AlignedSidLength = (ULONG)LongAlign(NextSidLength);

        if (NextSidLength > RemainingLength) {
            return STATUS_BUFFER_TOO_SMALL;
        }

        RemainingLength -= AlignedSidLength;

        TargetArrayElement[Index].Sid = NextSid;
        TargetArrayElement[Index].Attributes = Source[Index].Attributes;

        RtlCopySid( NextSidLength, NextSid, Source[Index].Sid );

        NextSid = (PSID)((ULONG)NextSid + AlignedSidLength);

        Index += 1;

    } //end_while

    (*NextTargetSid) = NextSid;
    (*RemainingTargetSidBufferSize) = RemainingLength;

    return STATUS_SUCCESS;

}


NTSTATUS
RtlConvertSidToUnicodeString(
    PUNICODE_STRING UnicodeString,
    PSID Sid,
    BOOLEAN AllocateDestinationString
    )

/*++

Routine Description:


    This function generates a printable unicode string representation
    of a SID.

    The resulting string will take one of two forms.  If the
    IdentifierAuthority value is not greater than 2^32, then
    the SID will be in the form:


        S-1-281736-12-72-9-110
              ^    ^^ ^^ ^ ^^^
              |     |  | |  |
              +-----+--+-+--+---- Decimal



    Otherwise it will take the form:


        S-1-0x173495281736-12-72-9-110
            ^^^^^^^^^^^^^^ ^^ ^^ ^ ^^^
             Hexidecimal    |  | |  |
                            +--+-+--+---- Decimal






Arguments:



    UnicodeString - Returns a unicode string that is equivalent to
        the SID. The maximum length field is only set if
        AllocateDestinationString is TRUE.

    Sid - Supplies the SID that is to be converted to unicode.

    AllocateDestinationString - Supplies a flag that controls whether or
        not this API allocates the buffer space for the destination
        string.  If it does, then the buffer must be deallocated using
        RtlFreeUnicodeString (note that only storage for
        DestinationString->Buffer is allocated by this API).

Return Value:

    SUCCESS - The conversion was successful

    STATUS_INVALID_SID - The sid provided does not have a valid structure,
        or has too many sub-authorities (more than SID_MAX_SUB_AUTHORITIES).

    STATUS_NO_MEMORY - There was not sufficient memory to allocate the
        target string.  This is returned only if AllocateDestinationString
        is specified as TRUE.

    STATUS_BUFFER_OVERFLOW - This is returned only if
        AllocateDestinationString is specified as FALSE.


--*/

{
    NTSTATUS Status;
    UCHAR Buffer[256];
    UCHAR String[256];

    UCHAR   i;
    ULONG   Tmp;

    PISID   iSid = (PISID)Sid;  // pointer to opaque structure

    ANSI_STRING AnsiString;

    RTL_PAGED_CODE();

    if (RtlValidSid( Sid ) != TRUE) {
        return(STATUS_INVALID_SID);
    }


    _snprintf(Buffer, sizeof(Buffer), "S-%u-", (USHORT)iSid->Revision );
    strcpy(String, Buffer);

    if (  (iSid->IdentifierAuthority.Value[0] != 0)  ||
          (iSid->IdentifierAuthority.Value[1] != 0)     ){
        _snprintf(Buffer, sizeof(Buffer), "0x%02hx%02hx%02hx%02hx%02hx%02hx",
                    (USHORT)iSid->IdentifierAuthority.Value[0],
                    (USHORT)iSid->IdentifierAuthority.Value[1],
                    (USHORT)iSid->IdentifierAuthority.Value[2],
                    (USHORT)iSid->IdentifierAuthority.Value[3],
                    (USHORT)iSid->IdentifierAuthority.Value[4],
                    (USHORT)iSid->IdentifierAuthority.Value[5] );
        strcat(String, Buffer);

    } else {

        Tmp = (ULONG)iSid->IdentifierAuthority.Value[5]          +
              (ULONG)(iSid->IdentifierAuthority.Value[4] <<  8)  +
              (ULONG)(iSid->IdentifierAuthority.Value[3] << 16)  +
              (ULONG)(iSid->IdentifierAuthority.Value[2] << 24);
        _snprintf(Buffer, sizeof(Buffer), "%lu", Tmp);
        strcat(String, Buffer);
    }


    for (i=0;i<iSid->SubAuthorityCount ;i++ ) {
        _snprintf(Buffer, sizeof(Buffer), "-%lu", iSid->SubAuthority[i]);
        strcat(String, Buffer);
    }

    //
    // Convert the string to a Unicode String
    //

    RtlInitString(&AnsiString, (PSZ) String);

    Status = RtlAnsiStringToUnicodeString( UnicodeString,
                                           &AnsiString,
                                           AllocateDestinationString
                                           );

    return(Status);
}




BOOLEAN
RtlEqualLuid (
    IN PLUID Luid1,
    IN PLUID Luid2
    )

/*++

Routine Description:

    This procedure test two LUID values for equality.

    This routine is here for backwards compatibility only. New code
    should use the macro.

Arguments:

    Luid1, Luid2 - Supply pointers to the two LUID values to compare.

Return Value:

    BOOLEAN - TRUE if the value of Luid1 is equal to Luid2, and FALSE
        otherwise.


--*/

{
    LUID UNALIGNED * TempLuid1;
    LUID UNALIGNED * TempLuid2;

    RTL_PAGED_CODE();

    return((Luid1->HighPart == Luid2->HighPart) &&
           (Luid1->LowPart  == Luid2->LowPart));

}


VOID
RtlCopyLuid (
    OUT PLUID DestinationLuid,
    IN PLUID SourceLuid
    )

/*++

Routine Description:

    This routine copies the value of the source LUID to the
    destination LUID.

Arguments:

    DestinationLuid - Receives a copy of the source Luid value.

    SourceLuid - Supplies the Luid value to be copied.  This LUID is
                 assumed to be structurally valid.

Return Value:

    None.

--*/

{
    RTL_PAGED_CODE();

    (*DestinationLuid) = (*SourceLuid);
    return;
}

VOID
RtlCopyLuidAndAttributesArray (
    IN ULONG ArrayLength,
    IN PLUID_AND_ATTRIBUTES Source,
    OUT PLUID_AND_ATTRIBUTES Target
    )

/*++

Routine Description:

    This routine copies the value of the source LUID_AND_ATTRIBUTES array
    to the target.

Arguments:

    ArrayLength - Number of elements in the source array to copy.

    Source - The source array.

    Target - Indicates where the array elements are to be copied to.


Return Value:

    None.


--*/

{

    ULONG Index = 0;

    RTL_PAGED_CODE();

    while (Index < ArrayLength) {

        Target[Index] = Source[Index];

        Index += 1;

    } //end_while


    return;

}

NTSTATUS
RtlCreateSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN ULONG Revision
    )

/*++

Routine Description:

    This procedure initializes a new "absolute format" security descriptor.
    After the procedure call the security descriptor is initialized with no
    system ACL, no discretionary ACL, no owner, no primary group and
    all control flags set to false (null).

Arguments:


    SecurityDescriptor - Supplies the security descriptor to
        initialize.

    Revision - Provides the revision level to assign to the security
        descriptor.  This should be one (1) for this release.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision level provided
        is not supported by this routine.


--*/

{
    RTL_PAGED_CODE();

    //
    // Check the requested revision
    //

    if (Revision == SECURITY_DESCRIPTOR_REVISION) {

        //
        // Typecast to the opaque SECURITY_DESCRIPTOR structure.
        //

        SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;

        ISecurityDescriptor->Revision = SECURITY_DESCRIPTOR_REVISION;
        ISecurityDescriptor->Sbz1     = 0;
        *(PUSHORT)(&ISecurityDescriptor->Control) = 0;
        ISecurityDescriptor->Owner = NULL;
        ISecurityDescriptor->Group = NULL;
        ISecurityDescriptor->Sacl  = NULL;
        ISecurityDescriptor->Dacl  = NULL;

        return STATUS_SUCCESS;
    }

    return STATUS_UNKNOWN_REVISION;
}


BOOLEAN
RtlValidSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor
    )

/*++

Routine Description:

    This procedure validates a SecurityDescriptor's structure.  This
    involves validating the revision levels of each component of the
    security descriptor.

Arguments:

    SecurityDescriptor - Pointer to the SECURITY_DESCRIPTOR structure
        to validate.

Return Value:

    BOOLEAN - TRUE if the structure of SecurityDescriptor is valid.


--*/

{
    PSID Owner;
    PSID Group;
    PACL Dacl;
    PACL Sacl;

    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;

    RTL_PAGED_CODE();

    try {

        //
        // known revision ?
        //

        if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
            return FALSE;
        }


        //
        // Validate each element contained in the security descriptor
        //

        if (ISecurityDescriptor->Owner != NULL) {
            Owner = RtlpOwnerAddrSecurityDescriptor( ISecurityDescriptor );
            if (!RtlValidSid( Owner )) {
                return FALSE;
            }
        }

        if (ISecurityDescriptor->Group != NULL) {
            Group = RtlpGroupAddrSecurityDescriptor( ISecurityDescriptor );
            if (!RtlValidSid( Group )) {
                return FALSE;
            }
        }

        if ( (ISecurityDescriptor->Control & SE_DACL_PRESENT) &&
             (ISecurityDescriptor->Dacl != NULL) ) {
            Dacl = RtlpDaclAddrSecurityDescriptor( ISecurityDescriptor );
            if (!RtlValidAcl( Dacl )) {
                return FALSE;
            }
        }

        if ( (ISecurityDescriptor->Control & SE_SACL_PRESENT) &&
             (ISecurityDescriptor->Sacl != NULL) ) {
            Sacl = RtlpSaclAddrSecurityDescriptor( ISecurityDescriptor );
            if (!RtlValidAcl( Sacl )) {
                return FALSE;
            }
        }

    } except(EXCEPTION_EXECUTE_HANDLER) {
        return FALSE;
    }

    //
    // All components are valid
    //

    return TRUE;


}


ULONG
RtlLengthSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor
    )

/*++

Routine Description:

    This routine returns the length, in bytes, necessary to capture a
    structurally valid SECURITY_DESCRIPTOR.  The length includes the length
    of all associated data structures (like SIDs and ACLs).  The length also
    takes into account the alignment requirements of each component.

    The minimum length of a security descriptor (one which has no associated
    SIDs or ACLs) is SECURITY_DESCRIPTOR_MIN_LENGTH.


Arguments:

    SecurityDescriptor - Points to the SECURITY_DESCRIPTOR whose
        length is to be returned.  The SECURITY_DESCRIPTOR's
        structure is assumed to be valid.

Return Value:

    ULONG - The length, in bytes, of the SECURITY_DESCRIPTOR.


--*/

{
    ULONG sum;


    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = (SECURITY_DESCRIPTOR *)SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // The length is the sum of the following:
    //
    //       SECURITY_DESCRIPTOR_MIN_LENGTH (or sizeof(SECURITY_DESCRIPTOR))
    //       length of Owner SID (if present)
    //       length of Group SID (if present)
    //       length of Discretionary ACL (if present and non-null)
    //       length of System ACL (if present and non-null)
    //

    sum = sizeof(SECURITY_DESCRIPTOR);

    //
    // Add in length of Owner SID
    //

    if (ISecurityDescriptor->Owner != NULL) {
        sum += (ULONG)(LongAlign(SeLengthSid(RtlpOwnerAddrSecurityDescriptor(ISecurityDescriptor))));
    }

    //
    // Add in length of Group SID
    //

    if (ISecurityDescriptor->Group != NULL) {
        sum += (ULONG)(LongAlign(SeLengthSid(RtlpGroupAddrSecurityDescriptor(ISecurityDescriptor))));
    }

    //
    // Add in used length of Discretionary ACL
    //

    if ( (ISecurityDescriptor->Control & SE_DACL_PRESENT) &&
         (ISecurityDescriptor->Dacl != NULL) ) {

        sum += (ULONG)(LongAlign(RtlpDaclAddrSecurityDescriptor(ISecurityDescriptor)->AclSize) );
    }

    //
    // Add in used length of System Acl
    //

    if ( (ISecurityDescriptor->Control & SE_SACL_PRESENT) &&
         (ISecurityDescriptor->Sacl != NULL) ) {
        sum += (ULONG)(LongAlign(RtlpSaclAddrSecurityDescriptor(ISecurityDescriptor)->AclSize) );
    }

    return sum;
}


ULONG
RtlLengthUsedSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor
    )

/*++

Routine Description:

    This routine returns the length, in bytes, in use in a structurally valid
    SECURITY_DESCRIPTOR.

    This is the number of bytes necessary to capture the security descriptor,
    which may be less the the current actual length of the security descriptor
    (RtlLengthSecurityDescriptor() is used to retrieve the actual length).

    Notice that the used length and actual length may differ if either the SACL
    or DACL include padding bytes.

    The length includes the length of all associated data structures (like SIDs
    and ACLs).  The length also takes into account the alignment requirements
    of each component.

    The minimum length of a security descriptor (one which has no associated
    SIDs or ACLs) is SECURITY_DESCRIPTOR_MIN_LENGTH.


Arguments:

    SecurityDescriptor - Points to the SECURITY_DESCRIPTOR whose used
        length is to be returned.  The SECURITY_DESCRIPTOR's
        structure is assumed to be valid.

Return Value:

    ULONG - Number of bytes of the SECURITY_DESCRIPTOR that are in use.


--*/

{
    ULONG sum;

    ACL_SIZE_INFORMATION AclSize;

    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = (SECURITY_DESCRIPTOR *)SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // The length is the sum of the following:
    //
    //       SECURITY_DESCRIPTOR_MIN_LENGTH (or sizeof(SECURITY_DESCRIPTOR))
    //       length of Owner SID (if present)
    //       length of Group SID (if present)
    //       length of Discretionary ACL (if present and non-null)
    //       length of System ACL (if present and non-null)
    //

    sum = sizeof(SECURITY_DESCRIPTOR);

    //
    // Add in length of Owner SID
    //

    if (ISecurityDescriptor->Owner != NULL) {
        sum += (ULONG)(LongAlign(SeLengthSid(RtlpOwnerAddrSecurityDescriptor(ISecurityDescriptor))));
    }

    //
    // Add in length of Group SID
    //

    if (ISecurityDescriptor->Group != NULL) {
        sum += (ULONG)(LongAlign(SeLengthSid(RtlpGroupAddrSecurityDescriptor(ISecurityDescriptor))));
    }

    //
    // Add in used length of Discretionary ACL
    //

    if ( (ISecurityDescriptor->Control & SE_DACL_PRESENT) &&
         (ISecurityDescriptor->Dacl != NULL) ) {

        RtlQueryInformationAcl( RtlpDaclAddrSecurityDescriptor(ISecurityDescriptor),
                                (PVOID)&AclSize,
                                sizeof(AclSize),
                                AclSizeInformation );

        sum += (ULONG)(LongAlign(AclSize.AclBytesInUse));
    }

    //
    // Add in used length of System Acl
    //

    if ( (ISecurityDescriptor->Control & SE_SACL_PRESENT) &&
         (ISecurityDescriptor->Sacl != NULL) ) {

        RtlQueryInformationAcl( RtlpSaclAddrSecurityDescriptor(ISecurityDescriptor),
                                (PVOID)&AclSize,
                                sizeof(AclSize),
                                AclSizeInformation );

        sum += (ULONG)(LongAlign(AclSize.AclBytesInUse));
    }

    return sum;
}



NTSTATUS
RtlSetAttributesSecurityDescriptor(
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN SECURITY_DESCRIPTOR_CONTROL Control,
    IN OUT PULONG Revision
    )
{
    RTL_PAGED_CODE();

    //
    // Always return the revision value - even if this isn't a valid
    // security descriptor
    //

    *Revision = ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Revision;

    if ( ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Revision
         != SECURITY_DESCRIPTOR_REVISION ) {
        return STATUS_UNKNOWN_REVISION;
    }

    //
    // Only allow setting SE_SERVER_SECURITY and SE_DACL_UNTRUSTED
    //

    if ( (Control & (SE_SERVER_SECURITY | SE_DACL_UNTRUSTED)) != Control ) {
        return STATUS_INVALID_PARAMETER ;
    }

    ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Control |= Control;

    return STATUS_SUCCESS;
}



NTSTATUS
RtlGetControlSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    OUT PSECURITY_DESCRIPTOR_CONTROL Control,
    OUT PULONG Revision
    )

/*++

Routine Description:

    This procedure retrieves the control information from a security descriptor.

Arguments:

    SecurityDescriptor - Supplies the security descriptor.

    Control - Receives the control information.

    Revision - Receives the revision of the security descriptor.
               This value will always be returned, even if an error
               is returned by this routine.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision of the security
        descriptor is not known to the routine.  It may be a newer
        revision than the routine knows about.


--*/

{
    RTL_PAGED_CODE();

    //
    // Always return the revision value - even if this isn't a valid
    // security descriptor
    //

    *Revision = ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Revision;


    if ( ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Revision
         != SECURITY_DESCRIPTOR_REVISION ) {
        return STATUS_UNKNOWN_REVISION;
    }


    *Control = ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Control;

    return STATUS_SUCCESS;

}


NTSTATUS
RtlSetDaclSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN BOOLEAN DaclPresent,
    IN PACL Dacl OPTIONAL,
    IN BOOLEAN DaclDefaulted OPTIONAL
    )

/*++

Routine Description:

    This procedure sets the discretionary ACL information of an absolute
    format security descriptor.  If there is already a discretionary ACL
    present in the security descriptor, it is superseded.

Arguments:

    SecurityDescriptor - Supplies the security descriptor to be which
        the discretionary ACL is to be added.

    DaclPresent - If FALSE, indicates the DaclPresent flag in the
        security descriptor should be set to FALSE.  In this case,
        the remaining optional parameters are ignored.  Otherwise,
        the DaclPresent control flag in the security descriptor is
        set to TRUE and the remaining optional parameters are not
        ignored.

    Dacl - Supplies the discretionary ACL for the security
        descriptor.  If this optional parameter is not passed, then a
        null ACL is assigned to the security descriptor.  A null
        discretionary ACL unconditionally grants access.  The ACL is
        referenced by, not copied into, by the security descriptor.

    DaclDefaulted - When set, indicates the discretionary ACL was
        picked up from some default mechanism (rather than explicitly
        specified by a user).  This value is set in the DaclDefaulted
        control flag in the security descriptor.  If this optional
        parameter is not passed, then the DaclDefaulted flag will be
        cleared.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision of the security
        descriptor is not known to the routine.  It may be a newer
        revision than the routine knows about.

    STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
        is not an absolute format security descriptor.


--*/

{

    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // Check the revision
    //

    if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
       return STATUS_UNKNOWN_REVISION;
    }

    //
    // Make sure the descriptor is absolute format
    //

    if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
        return STATUS_INVALID_SECURITY_DESCR;
    }

    //
    // Assign the DaclPresent flag value passed
    //


    if (DaclPresent) {

        ISecurityDescriptor->Control |= SE_DACL_PRESENT;

        //
        // Assign the ACL address if passed, otherwise set to null.
        //

        ISecurityDescriptor->Dacl = NULL;
        if (ARGUMENT_PRESENT(Dacl)) {
            ISecurityDescriptor->Dacl = Dacl;
        }




        //
        // Assign DaclDefaulted flag if passed, otherwise clear it.
        //

        ISecurityDescriptor->Control &= ~SE_DACL_DEFAULTED;
        if (DaclDefaulted == TRUE) {
            ISecurityDescriptor->Control |= SE_DACL_DEFAULTED;
        }
    } else {

        ISecurityDescriptor->Control &= ~SE_DACL_PRESENT;

    }


    return STATUS_SUCCESS;

}


NTSTATUS
RtlGetDaclSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    OUT PBOOLEAN DaclPresent,
    OUT PACL *Dacl,
    OUT PBOOLEAN DaclDefaulted
    )

/*++

Routine Description:

    This procedure retrieves the discretionary ACL information of a
    security descriptor.

Arguments:

    SecurityDescriptor - Supplies the security descriptor.

    DaclPresent - If TRUE, indicates that the security descriptor
        does contain a discretionary ACL.  In this case, the
        remaining OUT parameters will receive valid values.
        Otherwise, the security descriptor does not contain a
        discretionary ACL and the remaining OUT parameters will not
        receive valid values.

    Dacl - This value is returned only if the value returned for the
        DaclPresent flag is TRUE.  In this case, the Dacl parameter
        receives the address of the security descriptor's
        discretionary ACL.  If this value is returned as null, then
        the security descriptor has a null discretionary ACL.

    DaclDefaulted - This value is returned only if the value returned
        for the DaclPresent flag is TRUE.  In this case, the
        DaclDefaulted parameter receives the value of the security
        descriptor's DaclDefaulted control flag.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision of the security
        descriptor is not known to the routine.  It may be a newer
        revision than the routine knows about.


--*/

{
    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // Check the revision
    //

    if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
        return STATUS_UNKNOWN_REVISION;
    }

    //
    // Assign the DaclPresent flag value
    //

    *DaclPresent = RtlpAreControlBitsSet( ISecurityDescriptor, SE_DACL_PRESENT );

    if (*DaclPresent) {

        //
        // Assign the ACL address.
        //

        *Dacl = RtlpDaclAddrSecurityDescriptor(ISecurityDescriptor);

        //
        // Assign DaclDefaulted flag.
        //

        *DaclDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_DACL_DEFAULTED );
    }

    return STATUS_SUCCESS;

}


NTSTATUS
RtlSetSaclSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN BOOLEAN SaclPresent,
    IN PACL Sacl OPTIONAL,
    IN BOOLEAN SaclDefaulted OPTIONAL
    )

/*++

Routine Description:

    This procedure sets the system ACL information of an absolute security
    descriptor.  If there is already a system ACL present in the
    security descriptor, it is superseded.

Arguments:

    SecurityDescriptor - Supplies the security descriptor to be which
        the system ACL is to be added.

    SaclPresent - If FALSE, indicates the SaclPresent flag in the
        security descriptor should be set to FALSE.  In this case,
        the remaining optional parameters are ignored.  Otherwise,
        the SaclPresent control flag in the security descriptor is
        set to TRUE and the remaining optional parameters are not
        ignored.

    Sacl - Supplies the system ACL for the security descriptor.  If
        this optional parameter is not passed, then a null ACL is
        assigned to the security descriptor.  The ACL is referenced
        by, not copied into, by the security descriptor.

    SaclDefaulted - When set, indicates the system ACL was picked up
        from some default mechanism (rather than explicitly specified
        by a user).  This value is set in the SaclDefaulted control
        flag in the security descriptor.  If this optional parameter
        is not passed, then the SaclDefaulted flag will be cleared.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision of the security
        descriptor is not known to the routine.  It may be a newer
        revision than the routine knows about.

    STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
        is not an absolute format security descriptor.


--*/

{

    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // Check the revision
    //

    if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
        return STATUS_UNKNOWN_REVISION;
    }

    //
    // Make sure the descriptor is absolute format
    //

    if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
        return STATUS_INVALID_SECURITY_DESCR;
    }

    //
    // Assign the SaclPresent flag value passed
    //


    if (SaclPresent) {

        ISecurityDescriptor->Control |= SE_SACL_PRESENT;

        //
        // Assign the ACL address if passed, otherwise set to null.
        //

        ISecurityDescriptor->Sacl = NULL;
        if (ARGUMENT_PRESENT(Sacl)) {
           ISecurityDescriptor->Sacl = Sacl;
        }

        //
        // Assign SaclDefaulted flag if passed, otherwise clear it.
        //

        ISecurityDescriptor->Control &= ~ SE_SACL_DEFAULTED;
        if (ARGUMENT_PRESENT(SaclDefaulted)) {
            ISecurityDescriptor->Control |= SE_SACL_DEFAULTED;
        }
    } else {

        ISecurityDescriptor->Control &= ~SE_SACL_PRESENT;
    }

    return STATUS_SUCCESS;

}


NTSTATUS
RtlGetSaclSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    OUT PBOOLEAN SaclPresent,
    OUT PACL *Sacl,
    OUT PBOOLEAN SaclDefaulted
    )

/*++

Routine Description:

    This procedure retrieves the system ACL information of a security
    descriptor.

Arguments:

    SecurityDescriptor - Supplies the security descriptor.

    SaclPresent - If TRUE, indicates that the security descriptor
        does contain a system ACL.  In this case, the remaining OUT
        parameters will receive valid values.  Otherwise, the
        security descriptor does not contain a system ACL and the
        remaining OUT parameters will not receive valid values.

    Sacl - This value is returned only if the value returned for the
        SaclPresent flag is TRUE.  In this case, the Sacl parameter
        receives the address of the security descriptor's system ACL.
        If this value is returned as null, then the security
        descriptor has a null system ACL.

    SaclDefaulted - This value is returned only if the value returned
        for the SaclPresent flag is TRUE.  In this case, the
        SaclDefaulted parameter receives the value of the security
        descriptor's SaclDefaulted control flag.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision of the security
        descriptor is not known to the routine.  It may be a newer
        revision than the routine knows about.


--*/

{

    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // Check the revision
    //

    if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
        return STATUS_UNKNOWN_REVISION;
    }

    //
    // Assign the SaclPresent flag value
    //

    *SaclPresent = RtlpAreControlBitsSet( ISecurityDescriptor, SE_SACL_PRESENT );

    if (*SaclPresent) {

        //
        // Assign the ACL address.
        //

        *Sacl = RtlpSaclAddrSecurityDescriptor(ISecurityDescriptor);

        //
        // Assign SaclDefaulted flag.
        //

        *SaclDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_SACL_DEFAULTED );

    }

    return STATUS_SUCCESS;

}


NTSTATUS
RtlSetOwnerSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN PSID Owner OPTIONAL,
    IN BOOLEAN OwnerDefaulted OPTIONAL
    )

/*++

Routine Description:

    This procedure sets the owner information of an absolute security
    descriptor.  If there is already an owner present in the security
    descriptor, it is superseded.

Arguments:

    SecurityDescriptor - Supplies the security descriptor in which
        the owner is to be set.  If the security descriptor already
        includes an owner, it will be superseded by the new owner.

    Owner - Supplies the owner SID for the security descriptor.  If
        this optional parameter is not passed, then the owner is
        cleared (indicating the security descriptor has no owner).
        The SID is referenced by, not copied into, the security
        descriptor.

    OwnerDefaulted - When set, indicates the owner was picked up from
        some default mechanism (rather than explicitly specified by a
        user).  This value is set in the OwnerDefaulted control flag
        in the security descriptor.  If this optional parameter is
        not passed, then the SaclDefaulted flag will be cleared.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision of the security
        descriptor is not known to the routine.  It may be a newer
        revision than the routine knows about.

    STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
        is not an absolute format security descriptor.


--*/

{

    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // Check the revision
    //

    if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
        return STATUS_UNKNOWN_REVISION;
    }

    //
    // Make sure the descriptor is absolute format
    //

    if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
        return STATUS_INVALID_SECURITY_DESCR;
    }

    //
    // Assign the Owner field if passed, otherwise clear it.
    //

    ISecurityDescriptor->Owner = NULL;
    if (ARGUMENT_PRESENT(Owner)) {
        ISecurityDescriptor->Owner = Owner;
    }

    //
    // Assign the OwnerDefaulted flag if passed, otherwise clear it.
    //

    ISecurityDescriptor->Control &= ~SE_OWNER_DEFAULTED;
    if (OwnerDefaulted == TRUE) {
        ISecurityDescriptor->Control |= SE_OWNER_DEFAULTED;
    }

    return STATUS_SUCCESS;

}


NTSTATUS
RtlGetOwnerSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    OUT PSID *Owner,
    OUT PBOOLEAN OwnerDefaulted
    )

/*++

Routine Description:

    This procedure retrieves the owner information of a security
    descriptor.

Arguments:

    SecurityDescriptor - Supplies the security descriptor.

    Owner - Receives a pointer to the owner SID.  If the security
        descriptor does not currently contain an owner, then this
        value will be returned as null.  In this case, the remaining
        OUT parameters are not given valid return values.  Otherwise,
        this parameter points to an SID and the remaining OUT
        parameters are provided valid return values.

    OwnerDefaulted - This value is returned only if the value
        returned for the Owner parameter is not null.  In this case,
        the OwnerDefaulted parameter receives the value of the
        security descriptor's OwnerDefaulted control flag.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision of the security
        descriptor is not known to the routine.  It may be a newer
        revision than the routine knows about.


--*/

{

    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // Check the revision
    //

    if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
        return STATUS_UNKNOWN_REVISION;
    }

    //
    // Return the Owner field value.
    //

    *Owner = RtlpOwnerAddrSecurityDescriptor(ISecurityDescriptor);

    //
    // Return the OwnerDefaulted flag value.
    //

    *OwnerDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_OWNER_DEFAULTED );

    return STATUS_SUCCESS;

}


NTSTATUS
RtlSetGroupSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN PSID Group OPTIONAL,
    IN BOOLEAN GroupDefaulted OPTIONAL
    )

/*++

Routine Description:

    This procedure sets the primary group information of an absolute security
    descriptor.  If there is already an primary group present in the
    security descriptor, it is superseded.

Arguments:

    SecurityDescriptor - Supplies the security descriptor in which
        the primary group is to be set.  If the security descriptor
        already includes a primary group, it will be superseded by
        the new group.

    Group - Supplies the primary group SID for the security
        descriptor.  If this optional parameter is not passed, then
        the primary group is cleared (indicating the security
        descriptor has no primary group).  The SID is referenced by,
        not copied into, the security descriptor.

    GroupDefaulted - When set, indicates the owner was picked up from
        some default mechanism (rather than explicitly specified by a
        user).  This value is set in the OwnerDefaulted control flag
        in the security descriptor.  If this optional parameter is
        not passed, then the SaclDefaulted flag will be cleared.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision of the security
        descriptor is not known to the routine.  It may be a newer
        revision than the routine knows about.

    STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
        is not an absolute format security descriptor.


--*/

{

    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // Check the revision
    //

    if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
        return STATUS_UNKNOWN_REVISION;
    }

    //
    // Make sure the descriptor is absolute format
    //

    if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
        return STATUS_INVALID_SECURITY_DESCR;
    }

    //
    // Assign the Group field if passed, otherwise clear it.
    //

    ISecurityDescriptor->Group = NULL;
    if (ARGUMENT_PRESENT(Group)) {
        ISecurityDescriptor->Group = Group;
    }

    //
    // Assign the GroupDefaulted flag if passed, otherwise clear it.
    //

    ISecurityDescriptor->Control &= ~SE_GROUP_DEFAULTED;
    if (ARGUMENT_PRESENT(GroupDefaulted)) {
        ISecurityDescriptor->Control |= SE_GROUP_DEFAULTED;
    }

    return STATUS_SUCCESS;

}


NTSTATUS
RtlGetGroupSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    OUT PSID *Group,
    OUT PBOOLEAN GroupDefaulted
    )

/*++

Routine Description:

    This procedure retrieves the primary group information of a
    security descriptor.

Arguments:

    SecurityDescriptor - Supplies the security descriptor.

    Group - Receives a pointer to the primary group SID.  If the
        security descriptor does not currently contain a primary
        group, then this value will be returned as null.  In this
        case, the remaining OUT parameters are not given valid return
        values.  Otherwise, this parameter points to an SID and the
        remaining OUT parameters are provided valid return values.

    GroupDefaulted - This value is returned only if the value
        returned for the Group parameter is not null.  In this case,
        the GroupDefaulted parameter receives the value of the
        security descriptor's GroupDefaulted control flag.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision of the security
        descriptor is not known to the routine.  It may be a newer
        revision than the routine knows about.


--*/

{

    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor =
        (SECURITY_DESCRIPTOR *)SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // Check the revision
    //

    if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
        return STATUS_UNKNOWN_REVISION;
    }

    //
    // Return the Group field value.
    //

    *Group = RtlpGroupAddrSecurityDescriptor(ISecurityDescriptor);

    //
    // Return the GroupDefaulted flag value.
    //

    *GroupDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_GROUP_DEFAULTED );

    return STATUS_SUCCESS;

}


BOOLEAN
RtlAreAllAccessesGranted(
    IN ACCESS_MASK GrantedAccess,
    IN ACCESS_MASK DesiredAccess
    )

/*++

Routine Description:

    This routine is used to check a desired access mask against a
    granted access mask.  It is used by the Object Management
    component when dereferencing a handle.

Arguments:

        GrantedAccess - Specifies the granted access mask.

        DesiredAccess - Specifies the desired access mask.

Return Value:

    BOOLEAN - TRUE if the GrantedAccess mask has all the bits set
        that the DesiredAccess mask has set.  That is, TRUE is
        returned if all of the desired accesses have been granted.

--*/

{
    RTL_PAGED_CODE();

    return ((BOOLEAN)((~(GrantedAccess) & (DesiredAccess)) == 0));
}


BOOLEAN
RtlAreAnyAccessesGranted(
    IN ACCESS_MASK GrantedAccess,
    IN ACCESS_MASK DesiredAccess
    )

/*++

Routine Description:

    This routine is used to test whether any of a set of desired
    accesses are granted by a granted access mask.  It is used by
    components other than the the Object Management component for
    checking access mask subsets.

Arguments:

        GrantedAccess - Specifies the granted access mask.

        DesiredAccess - Specifies the desired access mask.

Return Value:

    BOOLEAN - TRUE if the GrantedAccess mask contains any of the bits
        specified in the DesiredAccess mask.  That is, if any of the
        desired accesses have been granted, TRUE is returned.


--*/

{
    RTL_PAGED_CODE();

    return ((BOOLEAN)(((GrantedAccess) & (DesiredAccess)) != 0));
}


VOID
RtlMapGenericMask(
    IN OUT PACCESS_MASK AccessMask,
    IN PGENERIC_MAPPING GenericMapping
    )

/*++

Routine Description:

    This routine maps all generic accesses in the provided access mask
    to specific and standard accesses according to the provided
    GenericMapping.

Arguments:

        AccessMask - Points to the access mask to be mapped.

        GenericMapping - The mapping of generic to specific and standard
                         access types.

Return Value:

    None.

--*/

{
    RTL_PAGED_CODE();

//    //
//    // Make sure the pointer is properly aligned
//    //
//
//    ASSERT( ((ULONG)AccessMask >> 2) << 2 == (ULONG)AccessMask );

    if (*AccessMask & GENERIC_READ) {

        *AccessMask |= GenericMapping->GenericRead;
    }

    if (*AccessMask & GENERIC_WRITE) {

        *AccessMask |= GenericMapping->GenericWrite;
    }

    if (*AccessMask & GENERIC_EXECUTE) {

        *AccessMask |= GenericMapping->GenericExecute;
    }

    if (*AccessMask & GENERIC_ALL) {

        *AccessMask |= GenericMapping->GenericAll;
    }

    //
    // Now clear the generic flags
    //

    *AccessMask &= ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);

    return;
}

NTSTATUS
RtlImpersonateSelf(
    IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
    )

/*++

Routine Description:

    This routine may be used to obtain an Impersonation token representing
    your own process's context.  This may be useful for enabling a privilege
    for a single thread rather than for the entire process; or changing
    the default DACL for a single thread.

    The token is assigned to the callers thread.



Arguments:

    ImpersonationLevel - The level to make the impersonation token.



Return Value:

    STATUS_SUCCESS -  The thread is now impersonating the calling process.

    Other - Status values returned by:

            NtOpenProcessToken()
            NtDuplicateToken()
            NtSetInformationThread()

--*/

{
    NTSTATUS
        Status,
        IgnoreStatus;

    HANDLE
        Token1,
        Token2;

    OBJECT_ATTRIBUTES
        ObjectAttributes;

    SECURITY_QUALITY_OF_SERVICE
        Qos;


    RTL_PAGED_CODE();

    InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);

    Qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
    Qos.ImpersonationLevel = ImpersonationLevel;
    Qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
    Qos.EffectiveOnly = FALSE;
    ObjectAttributes.SecurityQualityOfService = &Qos;

    Status = NtOpenProcessToken( NtCurrentProcess(), TOKEN_DUPLICATE, &Token1 );

    if (NT_SUCCESS(Status)) {
        Status = NtDuplicateToken(
                     Token1,
                     TOKEN_IMPERSONATE,
                     &ObjectAttributes,
                     FALSE,                 //EffectiveOnly
                     TokenImpersonation,
                     &Token2
                     );
        if (NT_SUCCESS(Status)) {
            Status = NtSetInformationThread(
                         NtCurrentThread(),
                         ThreadImpersonationToken,
                         &Token2,
                         sizeof(HANDLE)
                         );

            IgnoreStatus = NtClose( Token2 );
        }


        IgnoreStatus = NtClose( Token1 );
    }


    return(Status);

}

#ifndef WIN16



BOOLEAN
RtlpValidOwnerSubjectContext(
    IN HANDLE Token,
    IN PSID Owner,
    IN BOOLEAN ServerObject,
    OUT PNTSTATUS ReturnStatus
    )
/*++

Routine Description:

    This routine checks to see whether the provided SID is one the subject
    is authorized to assign as the owner of objects.

Arguments:

    Token - Points to the subject's effective token

    Owner - Points to the SID to be checked.

    ServerObject - Boolean indicating whether or not this is a server
       object, meaning it is protected by a primary-client combination.

    ReturnStatus - Status to be passed back to the caller on failure.

Return Value:

    FALSE on failure.

--*/

{

    ULONG Index;
    BOOLEAN Found;
    ULONG ReturnLength;
    PTOKEN_GROUPS GroupIds = NULL;
    PTOKEN_USER UserId = NULL;
    BOOLEAN rc = FALSE;
    PVOID HeapHandle;
    HANDLE TokenToUse;

    RTL_PAGED_CODE();

    //
    // Get the handle to the current process heap
    //

    if ( Owner == NULL ) {
        return(FALSE);
    }

    //
    // If it's not a server object, check the owner against the contents of the
    // client token.  If it is a server object, the owner must be valid in the
    // primary token.
    //

    if (!ServerObject) {

        TokenToUse = Token;

    } else {

        *ReturnStatus = NtOpenProcessToken(
                            NtCurrentProcess(),
                            TOKEN_QUERY,
                            &TokenToUse
                            );

        if (!NT_SUCCESS( *ReturnStatus )) {
            return( FALSE );
        }
    }

    HeapHandle = RtlProcessHeap();

    //
    //  Get the User from the Token
    //

    *ReturnStatus = NtQueryInformationToken(
                         TokenToUse,
                         TokenUser,
                         UserId,
                         0,
                         &ReturnLength
                         );

    if (!NT_SUCCESS( *ReturnStatus ) && (STATUS_BUFFER_TOO_SMALL != *ReturnStatus)) {
        if (ServerObject) {
            NtClose( TokenToUse );
        }
        return( FALSE );

    }

    UserId = RtlAllocateHeap( HeapHandle, 0, ReturnLength );

    if (UserId == NULL) {

        *ReturnStatus = STATUS_NO_MEMORY;
        if (ServerObject) {
            NtClose( TokenToUse );
        }

        return( FALSE );
    }

    *ReturnStatus = NtQueryInformationToken(
                         TokenToUse,
                         TokenUser,
                         UserId,
                         ReturnLength,
                         &ReturnLength
                         );

    if (!NT_SUCCESS( *ReturnStatus )) {
        if (ServerObject) {
            NtClose( TokenToUse );
        }
        return( FALSE );
    }

    if ( RtlEqualSid( Owner, UserId->User.Sid ) ) {

        RtlFreeHeap( HeapHandle, 0, (PVOID)UserId );
        if (ServerObject) {
            NtClose( TokenToUse );
        }
        return( TRUE );
    }

    RtlFreeHeap( HeapHandle, 0, (PVOID)UserId );

    //
    // Get the groups from the Token
    //

    *ReturnStatus = NtQueryInformationToken(
                         TokenToUse,
                         TokenGroups,
                         GroupIds,
                         0,
                         &ReturnLength
                         );

    if (!NT_SUCCESS( *ReturnStatus ) && (STATUS_BUFFER_TOO_SMALL != *ReturnStatus)) {

        if (ServerObject) {
            NtClose( TokenToUse );
        }
        return( FALSE );
    }

    GroupIds = RtlAllocateHeap( HeapHandle, 0, ReturnLength );

    if (GroupIds == NULL) {

        *ReturnStatus = STATUS_NO_MEMORY;
        if (ServerObject) {
            NtClose( TokenToUse );
        }
        return( FALSE );
    }

    *ReturnStatus = NtQueryInformationToken(
                         TokenToUse,
                         TokenGroups,
                         GroupIds,
                         ReturnLength,
                         &ReturnLength
                         );

    if (ServerObject) {
        NtClose( TokenToUse );
    }

    if (!NT_SUCCESS( *ReturnStatus )) {
        RtlFreeHeap( HeapHandle, 0, GroupIds );
        return( FALSE );
    }

    //
    //  Walk through the list of group IDs looking for a match to
    //  the specified SID.  If one is found, make sure it may be
    //  assigned as an owner.
    //
    //  This code is similar to that performed to set the default
    //  owner of a token (NtSetInformationToken).
    //

    Index = 0;
    while (Index < GroupIds->GroupCount) {

        Found = RtlEqualSid(
                    Owner,
                    GroupIds->Groups[Index].Sid
                    );

        if ( Found ) {

            if ( RtlpIdAssignableAsOwner(GroupIds->Groups[Index])) {

                RtlFreeHeap( HeapHandle, 0, GroupIds );
                return( TRUE );

            } else {

                RtlFreeHeap( HeapHandle, 0, GroupIds );
                return( FALSE );

            } //endif assignable

        }  //endif Found

        Index++;

    } //endwhile

    RtlFreeHeap( HeapHandle, 0, GroupIds );

    return ( FALSE  );
}

#if 0

BOOLEAN
RtlpValidOwnerSubjectContext(
    IN HANDLE Token,
    IN PSID Owner,
    BOOLEAN Dummy,
    OUT PNTSTATUS ReturnStatus
    )
/*++

Routine Description:

    This routine checks to see whether the provided SID is one the subject
    is authorized to assign as the owner of objects.

Arguments:

    Token - Points to the subject's effective token

    Owner - Points to the SID to be checked.



Return Value:

    none.

--*/

{

    ULONG Index;
    BOOLEAN Found;
    ULONG ReturnLength;
    PTOKEN_GROUPS GroupIds = NULL;
    PTOKEN_USER UserId = NULL;
    BOOLEAN rc = FALSE;
    NTSTATUS Status;
    PVOID HeapHandle;

    RTL_PAGED_CODE();

    //
    // Get the handle to the current process heap
    //

    if ( Owner == NULL ) {
        return(FALSE);
    }

    HeapHandle = RtlProcessHeap();

    *ReturnStatus = STATUS_SUCCESS;

    //
    //  Get the User from the Token
    //

    Status = NtQueryInformationToken(
                 Token,                    // Handle
                 TokenUser,                // TokenInformationClass
                 UserId,                   // TokenInformation
                 0,                        // TokenInformationLength
                 &ReturnLength             // ReturnLength
                 );

    ASSERT(Status == STATUS_BUFFER_TOO_SMALL);

    UserId = RtlAllocateHeap( HeapHandle, 0, ReturnLength );

    if (UserId == NULL) {

        *ReturnStatus = STATUS_NO_MEMORY;
        return( FALSE );
    }


    Status = NtQueryInformationToken(
                 Token,                    // Handle
                 TokenUser,                // TokenInformationClass
                 UserId,                   // TokenInformation
                 ReturnLength,             // TokenInformationLength
                 &ReturnLength             // ReturnLength
                 );

    if (!NT_SUCCESS( Status )) {
        *ReturnStatus = Status;
        return( FALSE );
    }

    if ( RtlEqualSid( Owner, UserId->User.Sid ) ) {

        RtlFreeHeap( HeapHandle, 0, (PVOID)UserId );
        return( TRUE );
    }

    RtlFreeHeap( HeapHandle, 0, (PVOID)UserId );

    //
    // Get the groups from the Token
    //

    Status = NtQueryInformationToken(
                 Token,                    // Handle
                 TokenGroups,              // TokenInformationClass
                 GroupIds,                 // TokenInformation
                 0,                        // TokenInformationLength
                 &ReturnLength             // ReturnLength
                 );
    ASSERT(Status == STATUS_BUFFER_TOO_SMALL);


    GroupIds = RtlAllocateHeap( HeapHandle, 0, ReturnLength );

    if (GroupIds == NULL) {

        *ReturnStatus = STATUS_NO_MEMORY;
        return( FALSE );

    }

    Status = NtQueryInformationToken(
                 Token,                    // Handle
                 TokenGroups,              // TokenInformationClass
                 GroupIds,                 // TokenInformation
                 ReturnLength,             // TokenInformationLength
                 &ReturnLength             // ReturnLength
                 );


    if (!NT_SUCCESS( Status )) {
        RtlFreeHeap( HeapHandle, 0, GroupIds );
        *ReturnStatus = Status;
        return( FALSE );
    }

    //
    //  Walk through the list of group IDs looking for a match to
    //  the specified SID.  If one is found, make sure it may be
    //  assigned as an owner.
    //
    //  This code is similar to that performed to set the default
    //  owner of a token (NtSetInformationToken).
    //

    Index = 0;
    while (Index < GroupIds->GroupCount) {

        Found = RtlEqualSid(
                    Owner,
                    GroupIds->Groups[Index].Sid
                    );

        if ( Found ) {

            if ( RtlpIdAssignableAsOwner(GroupIds->Groups[Index])) {

                RtlFreeHeap( HeapHandle, 0, GroupIds );
                return( TRUE );

            } else {

                RtlFreeHeap( HeapHandle, 0, GroupIds );
                return( FALSE );

            } //endif assignable


        }  //endif Found


        Index++;

    } //endwhile

    RtlFreeHeap( HeapHandle, 0, GroupIds );

    return ( FALSE  );

}

#endif  // WIN16


#endif

#if 0


BOOLEAN
RtlpContainsCreatorOwnerSid(
    PKNOWN_ACE Ace
    )
/*++

Routine Description:

    Tests to see if the specified ACE contains the CreatorOwnerSid.

Arguments:

    Ace - Pointer to the ACE whose SID is be compared to the Creator Sid.
        This ACE is assumed to be valid and a known ACE type.

Return Value:

    TRUE - The creator sid is in the ACE.

    FALSE - The creator sid is not in the ACE.


--*/
{

    BOOLEAN IsEqual;


    ULONG CreatorSid[CREATOR_SID_SIZE];


    SID_IDENTIFIER_AUTHORITY  CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;

    RTL_PAGED_CODE();

    //
    //  This is gross and ugly, but it's better than allocating
    //  virtual memory to hold the CreatorSid, because that can
    //  fail, and propogating the error back is a tremendous pain
    //

    ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);

    //
    //  Allocate and initialize the universal SIDs
    //

    RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );

    *(RtlSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_OWNER_RID;

    IsEqual = RtlEqualSid(&Ace->SidStart, (PSID)CreatorSid );

    return( IsEqual );

}


BOOLEAN
RtlpContainsCreatorGroupSid(
    PKNOWN_ACE Ace
    )
/*++

Routine Description:

    Tests to see if the specified ACE contains the CreatorGroupSid.

Arguments:

    Ace - Pointer to the ACE whose SID is be compared to the Creator Sid.
        This ACE is assumed to be valid and a known ACE type.

Return Value:

    TRUE - The creator sid is in the ACE.

    FALSE - The creator sid is not in the ACE.


--*/
{

    BOOLEAN IsEqual;


    ULONG CreatorSid[CREATOR_SID_SIZE];


    SID_IDENTIFIER_AUTHORITY  CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;

    RTL_PAGED_CODE();

    //
    //  This is gross and ugly, but it's better than allocating
    //  virtual memory to hold the CreatorSid, because that can
    //  fail, and propogating the error back is a tremendous pain
    //

    ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);

    //
    //  Allocate and initialize the universal SIDs
    //

    RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );

    *(RtlSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_GROUP_RID;

    IsEqual = RtlEqualSid(&Ace->SidStart, (PSID)CreatorSid );

    return( IsEqual );
}


BOOLEAN
RtlpContainsCreatorOwnerServerSid(
    PKNOWN_COMPOUND_ACE Ace
    )
/*++

Routine Description:

    Tests to see if the specified ACE contains the CreatorClientSid.

Arguments:

    Ace - Pointer to the compound ACE whose Client SID is be compared to the
          Creator Client Sid.  This ACE is assumed to be valid and a known
          compound ACE type.

Return Value:

    TRUE - The creator sid is in the ACE.

    FALSE - The creator sid is not in the ACE.


--*/
{
    BOOLEAN IsEqual;
    ULONG CreatorSid[CREATOR_SID_SIZE];
    SID_IDENTIFIER_AUTHORITY  CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;

    RTL_PAGED_CODE();

    //
    //  This is gross and ugly, but it's better than allocating
    //  virtual memory to hold the ClientSid, because that can
    //  fail, and propogating the error back is a tremendous pain
    //

    ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);

    //
    //  Allocate and initialize the universal SID
    //

    RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
    *(RtlSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_OWNER_SERVER_RID;

    IsEqual = RtlEqualSid(RtlCompoundAceClientSid( Ace ), (PSID)CreatorSid );

    return( IsEqual );

}


BOOLEAN
RtlpContainsCreatorGroupServerSid(
    PKNOWN_COMPOUND_ACE Ace
    )
/*++

Routine Description:

    Tests to see if the specified ACE contains the CreatorServerSid.

Arguments:

    Ace - Pointer to the compound ACE whose Server SID is be compared to the
          Creator Server Sid.  This ACE is assumed to be valid and a known
          compound ACE type.

Return Value:

    TRUE - The creator Server sid is in the ACE.

    FALSE - The creator Server sid is not in the ACE.


--*/
{
    BOOLEAN IsEqual;
    ULONG CreatorSid[CREATOR_SID_SIZE];
    SID_IDENTIFIER_AUTHORITY  CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;

    RTL_PAGED_CODE();

    //
    //  This is gross and ugly, but it's better than allocating
    //  virtual memory to hold the CreatorSid, because that can
    //  fail, and propogating the error back is a tremendous pain
    //

    ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);

    //
    //  Allocate and initialize the universal SIDs
    //

    RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
    *(RtlSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_GROUP_SERVER_RID;

    IsEqual = RtlEqualSid(RtlCompoundAceServerSid(Ace), (PSID)CreatorSid );

    return( IsEqual );
}

#endif


VOID
RtlpApplyAclToObject (
    IN PACL Acl,
    IN PGENERIC_MAPPING GenericMapping
    )

/*++

Routine Description:

    This is a private routine that maps Access Masks of an ACL so that
    they are applicable to the object type the ACL is being applied to.

    Only known DSA ACEs are mapped.  Unknown ACE types are ignored.

    Only access types in the GenericAll mapping for the target object
    type will be non-zero upon return.

Arguments:

    Acl - Supplies the acl being applied.

    GenericMapping - Specifies the generic mapping to use.


Return Value:

    None.

--*/

{
//////////////////////////////////////////////////////////////////////////////
//                                                                          //
//   The logic in the ACL inheritance code must mirror the code for         //
//   inheritance in the executive (in seassign.c).  Do not make changes     //
//   here without also making changes in that module.                       //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

    ULONG i;

    PACE_HEADER Ace;

    RTL_PAGED_CODE();

    //
    //  First check if the acl is null
    //

    if (Acl == NULL) {

        return;

    }


    //
    // Now walk the ACL, mapping each ACE as we go.
    //

    for (i = 0, Ace = FirstAce(Acl);
         i < Acl->AceCount;
         i += 1, Ace = NextAce(Ace)) {

        if (IsMSAceType( Ace )) {

            RtlApplyAceToObject( Ace, GenericMapping );
        }

    }

    return;
}

#ifndef WIN16


NTSTATUS
RtlpInheritAcl (
    IN PACL Acl,
    IN BOOLEAN IsDirectoryObject,
    IN PSID OwnerSid,
    IN PSID GroupSid,
    IN PSID ServerOwnerSid OPTIONAL,
    IN PSID ServerGroupSid OPTIONAL,
    IN PGENERIC_MAPPING GenericMapping,
    OUT PACL *NewAcl
    )

/*++

Routine Description:

    This is a private routine that produces an inherited acl from
    a parent acl according to the rules of inheritance

Arguments:

    Acl - Supplies the acl being inherited.

    IsDirectoryObject - Specifies if the new acl is for a directory.

    OwnerSid - Specifies the owner Sid to use.

    GroupSid - Specifies the group SID to use.

    GenericMapping - Specifies the generic mapping to use.

    NewAcl - Receives a pointer to the new (inherited) acl.

Return Value:

    STATUS_SUCCESS - An inheritable ACL was successfully generated.

    STATUS_NO_INHERITANCE - An inheritable ACL was not successfully generated.
        This is a warning completion status.

    STATUS_BAD_INHERITANCE_ACL - Indicates the acl built was not a valid ACL.
        This can becaused by a number of things.  One of the more probable
        causes is the replacement of a CreatorId with an SID that didn't fit
        into the ACE or ACL.

    STATUS_UNKNOWN_REVISION - Indicates the source ACL is a revision that
        is unknown to this routine.

--*/

{
//////////////////////////////////////////////////////////////////////////////
//                                                                          //
//   The logic in the ACL inheritance code must mirror the code for         //
//   inheritance in the executive (in seassign.c).  Do not make changes     //
//   here without also making changes in that module.                       //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////


    NTSTATUS Status;
    ULONG NewAclLength;
    PVOID HeapHandle;

    RTL_PAGED_CODE();

    //
    // Get the handle to the current process heap
    //

    HeapHandle = RtlProcessHeap();

    //
    //  First check if the acl is null
    //

    if (Acl == NULL) {

        return STATUS_NO_INHERITANCE;

    }

    if (Acl->AclRevision != ACL_REVISION2 && Acl->AclRevision != ACL_REVISION3) {
        return STATUS_UNKNOWN_REVISION;
    }


    //
    // Generating an inheritable ACL is a two-pass operation.
    // First you must see if there is anything to inherit, and if so,
    // allocate enough room to hold it.  then you must actually copy
    // the generated ACEs.
    //

    Status = RtlpLengthInheritAcl(
                 Acl,
                 IsDirectoryObject,
                 OwnerSid,
                 GroupSid,
                 ServerOwnerSid,
                 ServerGroupSid,
                 GenericMapping,
                 &NewAclLength
                 );

    if ( !NT_SUCCESS(Status) ) {
        return Status;
    }
    if (NewAclLength == 0) {
        return STATUS_NO_INHERITANCE;
    }

    (*NewAcl) = RtlAllocateHeap( HeapHandle, 0, NewAclLength );
    if ((*NewAcl) == NULL ) {
        return( STATUS_NO_MEMORY );
    }



    RtlCreateAcl( (*NewAcl), NewAclLength, Acl->AclRevision ); // Used to be hardwired to ACL_REVISION2
    Status = RtlpGenerateInheritAcl(
                 Acl,
                 IsDirectoryObject,
                 OwnerSid,
                 GroupSid,
                 ServerOwnerSid,
                 ServerGroupSid,
                 GenericMapping,
                 (*NewAcl)
                 );

    if (!NT_SUCCESS(Status)) {
        RtlFreeHeap( HeapHandle, 0, *NewAcl );
    }

    return Status;
}


NTSTATUS
RtlpLengthInheritAcl(
    IN PACL Acl,
    IN BOOLEAN IsDirectoryObject,
    IN PSID ClientOwnerSid,
    IN PSID ClientGroupSid,
    IN PSID ServerOwnerSid OPTIONAL,
    IN PSID ServerGroupSid OPTIONAL,
    IN PGENERIC_MAPPING GenericMapping,
    OUT PULONG NewAclLength
    )

/*++

Routine Description:

    This is a private routine that calculates the length needed to
    produce an inheritable ACL.

Arguments:

    Acl - Supplies the acl being inherited.

    IsDirectoryObject - Specifies if the new acl is for a directory.

    OwnerSid - Specifies the owner Sid to use.

    GroupSid - Specifies the group SID to use.

    GenericMapping - Specifies the generic mapping to use.

    NewAclLength - Receives the length of the inherited ACL.

Return Value:

    STATUS_SUCCESS - An inheritable ACL buffer successfully allocated.

    STATUS_NO_INHERITANCE - An inheritable ACL was not successfully generated.
        This is a warning completion status.

    STATUS_BAD_INHERITANCE_ACL - Indicates the acl built was not a valid ACL.
        This can becaused by a number of things.  One of the more probable
        causes is the replacement of a CreatorId with an SID that didn't fit
        into the ACE or ACL.


--*/

{
//////////////////////////////////////////////////////////////////////////////
//                                                                          //
//   The logic in the ACL inheritance code must mirror the code for         //
//   inheritance in the executive (in seassign.c).  Do not make changes     //
//   here without also making changes in that module.                       //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////


    NTSTATUS Status;
    ULONG i;

    ULONG NewAclSize, NewAceSize;

    PACE_HEADER OldAce;


    RTL_PAGED_CODE();

    //
    // Calculate the length needed to store any inherited ACEs
    // (this doesn't include the ACL header itself).
    //

    for (i = 0, OldAce = FirstAce(Acl), NewAclSize = 0;
         i < Acl->AceCount;
         i += 1, OldAce = NextAce(OldAce)) {

        //
        //  RtlpLengthInheritedAce will return the number of bytes needed
        //  to inherit a single ACE.
        //

        Status = RtlpLengthInheritedAce(
                     OldAce,
                     IsDirectoryObject,
                     ClientOwnerSid,
                     ClientGroupSid,
                     ServerOwnerSid,
                     ServerGroupSid,
                     GenericMapping,
                     &NewAceSize
                     );

        if ( !NT_SUCCESS(Status) ) {
            return Status;
        }

        NewAclSize += NewAceSize;

    }

    //
    //  Check to make sure there is something inheritable
    //

    if (NewAclSize == 0) {
        return STATUS_NO_INHERITANCE;
    }

    //
    // And make sure we don't exceed the length limitations of an ACL (WORD)
    //

    NewAclSize += sizeof(ACL);

    if (NewAclSize > 0xFFFF) {
        return(STATUS_BAD_INHERITANCE_ACL);
    }

    (*NewAclLength) = NewAclSize;

    return STATUS_SUCCESS;
}


NTSTATUS
RtlpGenerateInheritAcl(
    IN PACL Acl,
    IN BOOLEAN IsDirectoryObject,
    IN PSID ClientOwnerSid,
    IN PSID ClientGroupSid,
    IN PSID ServerOwnerSid OPTIONAL,
    IN PSID ServerGroupSid OPTIONAL,
    IN PGENERIC_MAPPING GenericMapping,
    OUT PACL NewAcl
    )

/*++

Routine Description:

    This is a private routine that produces an inheritable ACL.
    It is expected that RtlpLengthInheritAcl() has already been
    called to validate the inheritance and to indicate the length
    of buffer needed to perform the inheritance.

Arguments:

    Acl - Supplies the acl being inherited.

    IsDirectoryObject - Specifies if the new acl is for a directory.

    OwnerSid - Specifies the owner Sid to use.

    GroupSid - Specifies the group SID to use.

    GenericMapping - Specifies the generic mapping to use.

    NewAcl - Provides a pointer to the buffer to receive the new
        (inherited) acl.  This ACL must already be initialized.


Return Value:

    STATUS_SUCCESS - An inheritable ACL has been generated.

    STATUS_NO_INHERITANCE - An inheritable ACL was not successfully generated.
        This is a warning completion status.

    STATUS_BAD_INHERITANCE_ACL - Indicates the acl built was not a valid ACL.
        This can becaused by a number of things.  One of the more probable
        causes is the replacement of a CreatorId with an SID that didn't fit
        into the ACE or ACL.


--*/

{
//////////////////////////////////////////////////////////////////////////////
//                                                                          //
//   The logic in the ACL inheritance code must mirror the code for         //
//   inheritance in the executive (in seassign.c).  Do not make changes     //
//   here without also making changes in that module.                       //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////


    NTSTATUS Status;
    ULONG i;

    PACE_HEADER OldAce;


    RTL_PAGED_CODE();

    //
    // Walk through the original ACL generating any necessary
    // inheritable ACEs.
    //

    for (i = 0, OldAce = FirstAce(Acl);
         i < Acl->AceCount;
         i += 1, OldAce = NextAce(OldAce)) {

        //
        //  RtlpGenerateInheritedAce() will generate the ACE(s) necessary
        //  to inherit a single ACE.  This may be 0, 1, or more ACEs.
        //

        Status = RtlpGenerateInheritedAce(
                     OldAce,
                     IsDirectoryObject,
                     ClientOwnerSid,
                     ClientGroupSid,
                     ServerOwnerSid,
                     ServerGroupSid,
                     GenericMapping,
                     NewAcl
                     );

        if ( !NT_SUCCESS(Status) ) {
            return Status;
        }

    }


    return STATUS_SUCCESS;

}

#endif // WIN16

NTSTATUS
RtlpLengthInheritedAce (
    IN PACE_HEADER Ace,
    IN BOOLEAN IsDirectoryObject,
    IN PSID ClientOwnerSid,
    IN PSID ClientGroupSid,
    IN PSID ServerOwnerSid OPTIONAL,
    IN PSID ServerGroupSid OPTIONAL,
    IN PGENERIC_MAPPING GenericMapping,
    IN PULONG NewAceLength
    )

/*++

Routine Description:

    This is a private routine that calculates the number of bytes needed
    to allow for the inheritance of the specified ACE.  If the ACE is not
    inheritable, or its AccessMask ends up with no accesses, then the
    size may be returned as zero.

Arguments:

    Ace - Supplies the ace being checked

    IsDirectoryObject - Specifies if the new ace is for a directory

    ClientOwnerSid - Pointer to Sid to be assigned as the new owner.

    ClientGroupSid - Points to SID to be assigned as the new primary group.

    ServerOwnerSid - Provides the SID of a server to substitute into
        compound ACEs (if any that require editing are encountered).
        If this parameter is not provided, the SID passed for ClientOwnerSid
        will be used.

    ServerGroupSid - Provides the SID of a client to substitute into
        compound ACEs (if any that require editing are encountered).
        If this parameter is not provided, the SID passed for ClientGroupSid
        will be used.

    GenericMapping - Specifies the generic mapping to use.

    NewAceLength - Receives the length (number of bytes) needed to allow for
        the inheritance of the specified ACE.  This might be zero.

Return Value:

    STATUS_SUCCESS - The length needed has been calculated.

    STATUS_BAD_INHERITANCE_ACL - Indicates inheritance of the ace would
        result in an invalid ACL structure.  For example, an SID substitution
        for a known ACE type which has a CreatorOwner SID might exceed the
        length limits of an ACE.

    STATUS_INVALID_PARAMETER - An optional parameter was required, but not
        provided.

--*/

{
//////////////////////////////////////////////////////////////////////////////
//                                                                          //
//   The logic in the ACL inheritance code must mirror the code for         //
//   inheritance in the executive (in seassign.c).  Do not make changes     //
//   here without also making changes in that module.                       //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////



    ///////////////////////////////////////////////////////////////////////////
    //                                                                       //
    // !!!!!!!!!  This is tricky  !!!!!!!!!!                                 //
    //                                                                       //
    // The inheritence flags AND the sid of the ACE determine whether        //
    // we need 0, 1, or 2 ACEs.                                              //
    //                                                                       //
    // BE CAREFUL WHEN CHANGING THIS CODE.  READ THE DSA ACL ARCHITECTURE    //
    // SECTION COVERING INHERITENCE BEFORE ASSUMING YOU KNOW WHAT THE HELL   //
    // YOU ARE DOING!!!!                                                     //
    //                                                                       //
    // The general gist of the algorithm is:                                 //
    //                                                                       //
    //       if ( (container  && ContainerInherit) ||                        //
    //            (!container && ObjectInherit)      ) {                     //
    //               GenerateEffectiveAce;                                   //
    //       }                                                               //
    //                                                                       //
    //                                                                       //
    //       if (Container && Propagate) {                                   //
    //           Propogate copy of ACE and set InheritOnly;                  //
    //       }                                                               //
    //                                                                       //
    //                                                                       //
    // A slightly more accurate description of this algorithm is:            //
    //                                                                       //
    //   IO  === InheritOnly flag                                            //
    //   CI  === ContainerInherit flag                                       //
    //   OI  === ObjectInherit flag                                          //
    //   NPI === NoPropagateInherit flag                                     //
    //                                                                       //
    //   if ( (container  && CI) ||                                          //
    //        (!container && OI)   ) {                                       //
    //       Copy Header of ACE;                                             //
    //       Clear IO, NPI, CI, OI;                                          //
    //                                                                       //
    //       if (KnownAceType) {                                             //
    //           if (SID is a creator ID) {                                  //
    //               Copy appropriate creator SID;                           //
    //           } else {                                                    //
    //               Copy SID of original;                                   //
    //           }                                                           //
    //                                                                       //
    //           Copy AccessMask of original;                                //
    //           MapGenericAccesses;                                         //
    //           if (AccessMask == 0) {                                      //
    //               discard new ACE;                                        //
    //           }                                                           //
    //                                                                       //
    //       } else {                                                        //
    //           Copy body of ACE;                                           //
    //       }                                                               //
    //                                                                       //
    //   }                                                                   //
    //                                                                       //
    //   if (!NPI) {                                                         //
    //       Copy ACE as is;                                                 //
    //       Set IO;                                                         //
    //   }                                                                   //
    //                                                                       //
    //                                                                       //
    //                                                                       //
    ///////////////////////////////////////////////////////////////////////////


    ULONG LengthRequired = 0;
    ACCESS_MASK LocalMask;

    PSID LocalServerOwner;
    PSID LocalServerGroup;
    PSID Sid;

    ULONG Rid;

    ULONG CreatorSid[CREATOR_SID_SIZE];
    ULONG GroupSid[CREATOR_SID_SIZE];
    ULONG CreatorOwnerServerSid[CREATOR_SID_SIZE];
    ULONG CreatorGroupServerSid[CREATOR_SID_SIZE];

    SID_IDENTIFIER_AUTHORITY  CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;


    RTL_PAGED_CODE();

    //
    //  This is gross and ugly, but it's better than allocating
    //  virtual memory to hold the ClientSid, because that can
    //  fail, and propogating the error back is a tremendous pain
    //

    ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);

    //
    // Allocate and initialize the universal SIDs we're going to need
    // to look for inheritable ACEs.
    //

    RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
    RtlInitializeSid( (PSID)GroupSid,   &CreatorSidAuthority, 1 );

    RtlInitializeSid( (PSID)CreatorOwnerServerSid, &CreatorSidAuthority, 1 );
    RtlInitializeSid( (PSID)CreatorGroupServerSid, &CreatorSidAuthority, 1 );

    *(RtlSubAuthoritySid( (PSID)CreatorSid, 0 ))            = SECURITY_CREATOR_OWNER_RID;
    *(RtlSubAuthoritySid( (PSID)GroupSid, 0 ))              = SECURITY_CREATOR_GROUP_RID;

    *(RtlSubAuthoritySid( (PSID)CreatorOwnerServerSid, 0 )) = SECURITY_CREATOR_OWNER_SERVER_RID;
    *(RtlSubAuthoritySid( (PSID)CreatorGroupServerSid, 0 )) = SECURITY_CREATOR_GROUP_SERVER_RID;

    //
    // Everywhere the pseudo-code above says "copy", the code in this
    // routine simply calculates the length of.  RtlpGenerateInheritedAce()
    // will actually be used to do the "copy".
    //

    LocalServerOwner = ARGUMENT_PRESENT(ServerOwnerSid) ? ServerOwnerSid : ClientOwnerSid;

    LocalServerGroup = ARGUMENT_PRESENT(ServerGroupSid) ? ServerGroupSid : ClientGroupSid;

    //
    //  check to see if we will have a protection ACE (one mapped to
    //  the target object type).
    //

    if ( (IsDirectoryObject && ContainerInherit(Ace)) ||
         (!IsDirectoryObject && ObjectInherit(Ace))     ) {

        LengthRequired = (ULONG)Ace->AceSize;

        if (IsKnownAceType( Ace ) ) {

            //
            // May need to adjust size due to SID substitution
            //

            PKNOWN_ACE KnownAce = (PKNOWN_ACE)Ace;

            Sid = &KnownAce->SidStart;

            if (RtlEqualPrefixSid ( Sid, CreatorSid )) {

                Rid = *RtlSubAuthoritySid( Sid, 0 );

                switch (Rid) {
                    case SECURITY_CREATOR_OWNER_RID:
                        {
                            LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientOwnerSid);
                            break;
                        }
                    case SECURITY_CREATOR_GROUP_RID:
                        {
                            LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientGroupSid);
                            break;
                        }
                    case SECURITY_CREATOR_OWNER_SERVER_RID:
                        {
                            LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ServerOwnerSid);
                            break;
                        }
                    case SECURITY_CREATOR_GROUP_SERVER_RID:
                        {
                            LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ServerGroupSid);
                            break;
                        }
                    default :
                        {
                            //
                            // We don't know what this SID is, do nothing and the original will be copied.
                            //

                            break;
                        }
                }
            }

            //
            // If after mapping the access mask, the access mask
            // is empty, then drop the ACE.
            //

            LocalMask = ((PKNOWN_ACE)(Ace))->Mask;
            RtlMapGenericMask( &LocalMask, GenericMapping);

            //
            // Mask off any bits that aren't meaningful
            //

            LocalMask &= ( STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL | ACCESS_SYSTEM_SECURITY );

            if (LocalMask == 0) {
                LengthRequired = 0;
            }

        } else {

            if (IsCompoundAceType(Ace)) {

                PKNOWN_COMPOUND_ACE KnownAce = (PKNOWN_COMPOUND_ACE)Ace;
                PSID AceServerSid;
                PSID AceClientSid;

                AceServerSid = RtlCompoundAceServerSid( KnownAce );
                AceClientSid = RtlCompoundAceClientSid( KnownAce );

                if (RtlEqualPrefixSid ( AceClientSid, CreatorSid )) {

                    Rid = *RtlSubAuthoritySid( AceClientSid, 0 );

                    switch (Rid) {
                        case SECURITY_CREATOR_OWNER_RID:
                            {
                                LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientOwnerSid);
                                break;
                            }
                        case SECURITY_CREATOR_GROUP_RID:
                            {
                                LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientGroupSid);
                                break;
                            }
                        case SECURITY_CREATOR_OWNER_SERVER_RID:
                            {
                                LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ServerOwnerSid);
                                break;
                            }
                        case SECURITY_CREATOR_GROUP_SERVER_RID:
                            {
                                LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ServerGroupSid);
                                break;
                            }
                        default :
                            {
                                //
                                // We don't know what this SID is, do nothing and the original will be copied.
                                //

                                break;
                            }
                    }
                }

                if (RtlEqualPrefixSid ( AceServerSid, CreatorSid )) {

                    Rid = *RtlSubAuthoritySid( AceServerSid, 0 );

                    switch (Rid) {
                        case SECURITY_CREATOR_OWNER_RID:
                            {
                                LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientOwnerSid);
                                break;
                            }
                        case SECURITY_CREATOR_GROUP_RID:
                            {
                                LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientGroupSid);
                                break;
                            }
                        case SECURITY_CREATOR_OWNER_SERVER_RID:
                            {
                                LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ServerOwnerSid);
                                break;
                            }
                        case SECURITY_CREATOR_GROUP_SERVER_RID:
                            {
                                LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ServerGroupSid);
                                break;
                            }
                        default :
                            {
                                //
                                // We don't know what this SID is, do nothing and the original will be copied.
                                //

                                break;
                            }
                    }
                }
            }

            //
            // If after mapping the access mask, the access mask
            // is empty, then drop the ACE.
            //

            LocalMask = ((PKNOWN_COMPOUND_ACE)(Ace))->Mask;
            RtlMapGenericMask( &LocalMask, GenericMapping);

            //
            // Mask off any bits that aren't meaningful
            //

            LocalMask &= ( STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL | ACCESS_SYSTEM_SECURITY );

            if (LocalMask == 0) {
                LengthRequired = 0;
            }
        }

        //
        // We have the length of the new ACE, but we've calculated
        // it with a ULONG.  It must fit into a USHORT.  See if it
        // does.
        //

        ASSERT(sizeof(Ace->AceSize) == 2);
        if (LengthRequired > 0xFFFF) {
            return STATUS_BAD_INHERITANCE_ACL;
        }
    }

    //
    // If we are inheriting onto a container, then we may need to
    // propagate the inheritance as well.
    //

    if (IsDirectoryObject && Propagate(Ace)) {

        LengthRequired += (ULONG)Ace->AceSize;
    }

    //
    //  Now return to our caller
    //

    (*NewAceLength) = LengthRequired;
    return STATUS_SUCCESS;

}


NTSTATUS
RtlpGenerateInheritedAce (
    IN PACE_HEADER OldAce,
    IN BOOLEAN IsDirectoryObject,
    IN PSID ClientOwnerSid,
    IN PSID ClientGroupSid,
    IN PSID ServerOwnerSid OPTIONAL,
    IN PSID ServerGroupSid OPTIONAL,
    IN PGENERIC_MAPPING GenericMapping,
    OUT PACL NewAcl
    )

/*++

Routine Description:

    This is a private routine that checks if the input ace is inheritable
    and produces 0, 1, or 2 inherited aces in the given buffer.

    See RtlpLengthInheritedAce() for detailed information on ACE inheritance.

    THE CODE IN THIS ROUTINE MUST MATCH THE CODE IN RtlpLengthInheritanceAce()!!!

Arguments:

    OldAce - Supplies the ace being inherited

    IsDirectoryObject - Specifies if the new ACE is for a directory

    ClientOwnerSid - Specifies the owner Sid to use

    ClientGroupSid - Specifies the new Group Sid to use

    ServerSid - Optionally specifies the Server Sid to use in compound ACEs.

    ClientSid - Optionally specifies the Client Sid to use in compound ACEs.

    GenericMapping - Specifies the generic mapping to use

    NewAcl - Provides a pointer to the ACL into which the ACE is to be
        inherited.

Return Value:

    STATUS_SUCCESS - The ACE was inherited successfully.

    STATUS_BAD_INHERITANCE_ACL - Indicates something went wrong preventing
        the ACE from being inherited.  This generally represents a bugcheck
        situation when returned from this call.


--*/

{
//////////////////////////////////////////////////////////////////////////////
//                                                                          //
//   The logic in the ACL inheritance code must mirror the code for         //
//   inheritance in the executive (in seassign.c).  Do not make changes     //
//   here without also making changes in that module.                       //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////



    ACCESS_MASK LocalMask;
    PVOID AcePosition;
    PUCHAR Target;
    BOOLEAN CreatorOwner, CreatorGroup;
    BOOLEAN CreatorClient, CreatorServer;
    BOOLEAN ProtectionAceCopied;

    PSID LocalServerOwner;
    PSID LocalServerGroup;

    ULONG CreatorSid[CREATOR_SID_SIZE];

    SID_IDENTIFIER_AUTHORITY  CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;

    ULONG Rid;
    PSID SidToCopy;
    PSID ClientSidToCopy;
    PSID ServerSidToCopy;
    PSID AceClientSid;
    PSID AceServerSid;

    RTL_PAGED_CODE();

    //
    //  This is gross and ugly, but it's better than allocating
    //  virtual memory to hold the ClientSid, because that can
    //  fail, and propogating the error back is a tremendous pain
    //

    ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);

    //
    // Allocate and initialize the universal SIDs we're going to need
    // to look for inheritable ACEs.
    //

    RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
    *(RtlSubAuthoritySid( (PSID)CreatorSid, 0 ))            = SECURITY_CREATOR_OWNER_RID;

    if (!RtlFirstFreeAce( NewAcl, &AcePosition ) ) {
        return STATUS_BAD_INHERITANCE_ACL;
    }

    if (!ARGUMENT_PRESENT(ServerOwnerSid)) {
        LocalServerOwner = ClientOwnerSid;
    } else {
        LocalServerOwner = ServerOwnerSid;
    }

    if (!ARGUMENT_PRESENT(ServerGroupSid)) {
        LocalServerGroup = ClientGroupSid;
    } else {
        LocalServerGroup = ServerGroupSid;
    }

    //
    //  check to see if we will have a protection ACE (one mapped to
    //  the target object type).
    //

    if ( (IsDirectoryObject  && ContainerInherit(OldAce)) ||
         (!IsDirectoryObject && ObjectInherit(OldAce))      ) {

        ProtectionAceCopied = TRUE;

        if (IsKnownAceType( OldAce ) ) {

            //
            // If after mapping the access mask, the access mask
            // is empty, then drop the ACE.
            //

            LocalMask = ((PKNOWN_ACE)(OldAce))->Mask;
            RtlMapGenericMask( &LocalMask, GenericMapping);

            //
            // Mask off any bits that aren't meaningful
            //

            LocalMask &= ( STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL | ACCESS_SYSTEM_SECURITY );

            if (LocalMask == 0) {

                ProtectionAceCopied = FALSE;

            } else {

                PKNOWN_ACE KnownAce = (PKNOWN_ACE)OldAce;

                Target = (PUCHAR)AcePosition;

                //
                // See if the SID in the ACE is one of the various CREATOR_* SIDs by
                // comparing identifier authorities.
                //

                if (RtlEqualPrefixSid ( &KnownAce->SidStart, CreatorSid )) {

                    Rid = *RtlSubAuthoritySid( &KnownAce->SidStart, 0 );

                    switch (Rid) {
                        case SECURITY_CREATOR_OWNER_RID:
                            {
                                SidToCopy = ClientOwnerSid;
                                break;
                            }
                        case SECURITY_CREATOR_GROUP_RID:
                            {
                                SidToCopy = ClientGroupSid;
                                break;
                            }
                        case SECURITY_CREATOR_OWNER_SERVER_RID:
                            {
                                SidToCopy = LocalServerOwner;
                                break;
                            }
                        case SECURITY_CREATOR_GROUP_SERVER_RID:
                            {
                                SidToCopy = LocalServerGroup;
                                break;
                            }
                        default :
                            {
                                //
                                // We don't know what this SID is, just copy the original.
                                //

                                SidToCopy = &KnownAce->SidStart;
                                break;
                            }
                    }

                    //
                    // SID substitution required.  Copy all except the SID.
                    //

                    RtlMoveMemory(
                        Target,
                        OldAce,
                        FIELD_OFFSET(KNOWN_ACE, SidStart)
                        );

                    Target = (PUCHAR)(Target + FIELD_OFFSET(KNOWN_ACE, SidStart));

                    //
                    // Now copy the correct SID
                    //

                    RtlMoveMemory(
                        Target,
                        SidToCopy,
                        SeLengthSid(SidToCopy)
                        );

                    //
                    // Set the size of the ACE accordingly
                    //

                    ((PKNOWN_ACE)AcePosition)->Header.AceSize =
                        (USHORT)FIELD_OFFSET(KNOWN_ACE, SidStart) +
                        (USHORT)SeLengthSid(SidToCopy);

                } else {

                    //
                    // No ID substitution, copy ACE as is.
                    //

                    RtlMoveMemory(
                        Target,
                        OldAce,
                        ((PKNOWN_ACE)OldAce)->Header.AceSize
                        );
                }

                //
                // Put the mapped access mask in the new ACE
                //

                ((PKNOWN_ACE)AcePosition)->Mask = LocalMask;
            }

        } else if (IsCompoundAceType(OldAce)) {

            //
            // If after mapping the access mask, the access mask
            // is empty, then drop the ACE.
            //

            LocalMask = ((PKNOWN_COMPOUND_ACE)(OldAce))->Mask;
            RtlMapGenericMask( &LocalMask, GenericMapping);

            //
            // Mask off any bits that aren't meaningful
            //

            LocalMask &= ( STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL | ACCESS_SYSTEM_SECURITY );

            if (LocalMask == 0) {

                ProtectionAceCopied = FALSE;

            } else {

                Target = (PUCHAR)AcePosition;

                //
                // See if the SID in the ACE is one of the various CREATOR_* SIDs by
                // comparing identifier authorities.
                //

                AceServerSid = RtlCompoundAceServerSid( OldAce );
                AceClientSid = RtlCompoundAceClientSid( OldAce );

                if (RtlEqualPrefixSid ( AceServerSid, CreatorSid )) {

                    Rid = *RtlSubAuthoritySid( AceServerSid, 0 );

                    switch (Rid) {
                        case SECURITY_CREATOR_OWNER_RID:
                            {
                                ServerSidToCopy = ClientOwnerSid;
                                break;
                            }
                        case SECURITY_CREATOR_GROUP_RID:
                            {
                                ServerSidToCopy = ClientGroupSid;
                                break;
                            }
                        case SECURITY_CREATOR_OWNER_SERVER_RID:
                            {
                                ServerSidToCopy = LocalServerOwner;
                                break;
                            }
                        case SECURITY_CREATOR_GROUP_SERVER_RID:
                            {
                                ServerSidToCopy = LocalServerGroup;
                                break;
                            }
                        default :
                            {
                                //
                                // We don't know what this SID is, just copy the original.
                                //

                                ServerSidToCopy = AceServerSid;
                                break;
                            }
                    }

                } else {

                    ServerSidToCopy = AceServerSid;
                }

                if (RtlEqualPrefixSid ( AceClientSid, CreatorSid )) {

                    Rid = *RtlSubAuthoritySid( AceClientSid, 0 );

                    switch (Rid) {
                        case SECURITY_CREATOR_OWNER_RID:
                            {
                                ClientSidToCopy = ClientOwnerSid;
                                break;
                            }
                        case SECURITY_CREATOR_GROUP_RID:
                            {
                                ClientSidToCopy = ClientGroupSid;
                                break;
                            }
                        case SECURITY_CREATOR_OWNER_SERVER_RID:
                            {
                                ClientSidToCopy = LocalServerOwner;
                                break;
                            }
                        case SECURITY_CREATOR_GROUP_SERVER_RID:
                            {
                                ClientSidToCopy = LocalServerGroup;
                                break;
                            }
                        default :
                            {
                                //
                                // We don't know what this SID is, just copy the original.
                                //

                                ClientSidToCopy = AceClientSid;
                                break;
                            }
                    }

                } else {

                    ClientSidToCopy = AceClientSid;
                }

                //
                // Copy ACE in pieces.  Body of ACE first.
                //


                RtlMoveMemory(
                    Target,
                    OldAce,
                    FIELD_OFFSET(KNOWN_ACE, Mask)
                    );

                Target = (PUCHAR)(Target + FIELD_OFFSET(KNOWN_COMPOUND_ACE, SidStart));

                //
                // Now copy the correct Server SID
                //

                RtlMoveMemory(
                    Target,
                    ServerSidToCopy,
                    SeLengthSid(ServerSidToCopy)
                    );

                Target = (PUCHAR)(Target + SeLengthSid(ServerSidToCopy));

                //
                // Now copy the correct Client SID
                //

                RtlMoveMemory(
                    Target,
                    ClientSidToCopy,
                    SeLengthSid(ClientSidToCopy)
                    );

                //
                // Set the size of the ACE accordingly
                //

                ((PKNOWN_COMPOUND_ACE)AcePosition)->Header.AceSize =
                    (USHORT)FIELD_OFFSET(KNOWN_COMPOUND_ACE, SidStart) +
                    (USHORT)SeLengthSid(ServerSidToCopy) +
                    (USHORT)SeLengthSid(ClientSidToCopy);

                //
                // Put the mapped access mask in the new ACE and set the type information
                //

                ((PKNOWN_COMPOUND_ACE)AcePosition)->Mask = LocalMask;
                ((PKNOWN_COMPOUND_ACE)AcePosition)->Header.AceType = ACCESS_ALLOWED_COMPOUND_ACE_TYPE;
                ((PKNOWN_COMPOUND_ACE)AcePosition)->CompoundAceType = COMPOUND_ACE_IMPERSONATION;
            }

        } else {

            //
            // Not a known ACE type, copy ACE as is
            //

            RtlMoveMemory(
                AcePosition,
                OldAce,
                ((PKNOWN_COMPOUND_ACE)OldAce)->Header.AceSize
                );
        }

        //
        // If the ACE was actually kept, clear all the inherit flags
        // and update the ACE count of the ACL.
        //

        if (ProtectionAceCopied) {
            ((PACE_HEADER)AcePosition)->AceFlags &= ~VALID_INHERIT_FLAGS;
            NewAcl->AceCount += 1;
        }
    }

    //
    // If we are inheriting onto a container, then we may need to
    // propagate the inheritance as well.
    //

    if (IsDirectoryObject && Propagate(OldAce)) {

        //
        // copy it as is, but make sure the InheritOnly bit is set.
        //

        if (!RtlFirstFreeAce( NewAcl, &AcePosition ) ) {
            return STATUS_BAD_INHERITANCE_ACL;
        }

        RtlMoveMemory(
            AcePosition,
            OldAce,
            ((PKNOWN_ACE)OldAce)->Header.AceSize
            );

        ((PACE_HEADER)AcePosition)->AceFlags |= INHERIT_ONLY_ACE;
        NewAcl->AceCount += 1;
    }

    //
    //  Now return to our caller
    //

    return STATUS_SUCCESS;
}

#if DBG
NTSTATUS
RtlDumpUserSid(
    VOID
    )
{
    NTSTATUS Status;
    HANDLE   TokenHandle;
    CHAR     Buffer[200];
    ULONG    ReturnLength;
    PSID     pSid;
    UNICODE_STRING SidString;
    PTOKEN_USER  User;

    //
    // Attempt to open the impersonation token first
    //

    Status = NtOpenThreadToken(
                 NtCurrentThread(),
                 GENERIC_READ,
                 FALSE,
                 &TokenHandle
                 );

    if (!NT_SUCCESS( Status )) {

        DbgPrint("Not impersonating, status = %X, trying process token\n",Status);

        Status = NtOpenProcessToken(
                     NtCurrentProcess(),
                     GENERIC_READ,
                     &TokenHandle
                     );

        if (!NT_SUCCESS( Status )) {
            DbgPrint("Unable to open process token, status = %X\n",Status);
            return( Status );
        }
    }

    Status = NtQueryInformationToken (
                 TokenHandle,
                 TokenUser,
                 Buffer,
                 200,
                 &ReturnLength
                 );

    if (!NT_SUCCESS( Status )) {

        DbgPrint("Unable to query user sid, status = %X \n",Status);
        NtClose(TokenHandle);
        return( Status );
    }

    User = (PTOKEN_USER)Buffer;

    pSid = User->User.Sid;

    Status = RtlConvertSidToUnicodeString( &SidString, pSid, TRUE );

    if (!NT_SUCCESS( Status )) {
        DbgPrint("Unable to format sid string, status = %X \n",Status);
        NtClose(TokenHandle);
        return( Status );
    }

    DbgPrint("Current Sid = %wZ \n",&SidString);

    RtlFreeUnicodeString( &SidString );

    return( STATUS_SUCCESS );
}

#endif
